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            // ClickHouse: CHECK TABLE t [PARTITION p] [SETTINGS ...]
934            TokenType::Check
935                if matches!(
936                    self.config.dialect,
937                    Some(crate::dialects::DialectType::ClickHouse)
938                ) =>
939            {
940                self.skip(); // consume CHECK
941                self.parse_command()?
942                    .ok_or_else(|| self.parse_error("Failed to parse CHECK statement"))
943            }
944            // ClickHouse: SETTINGS key=value, ... (standalone statement or after another statement)
945            TokenType::Settings
946                if matches!(
947                    self.config.dialect,
948                    Some(crate::dialects::DialectType::ClickHouse)
949                ) =>
950            {
951                self.skip(); // consume SETTINGS
952                self.parse_command()?
953                    .ok_or_else(|| self.parse_error("Failed to parse SETTINGS statement"))
954            }
955            // ClickHouse: SYSTEM STOP/START MERGES, etc.
956            TokenType::System
957                if matches!(
958                    self.config.dialect,
959                    Some(crate::dialects::DialectType::ClickHouse)
960                ) =>
961            {
962                self.skip(); // consume SYSTEM
963                self.parse_command()?
964                    .ok_or_else(|| self.parse_error("Failed to parse SYSTEM statement"))
965            }
966            // ClickHouse: RENAME TABLE db.t1 TO db.t2 [, db.t3 TO db.t4 ...]
967            TokenType::Var
968                if self.peek().text.eq_ignore_ascii_case("RENAME")
969                    && matches!(
970                        self.config.dialect,
971                        Some(crate::dialects::DialectType::ClickHouse)
972                    ) =>
973            {
974                self.skip(); // consume RENAME
975                self.parse_command()?
976                    .ok_or_else(|| self.parse_error("Failed to parse RENAME statement"))
977            }
978            // ClickHouse: OPTIMIZE TABLE t [FINAL] [DEDUPLICATE [BY ...]]
979            TokenType::Var
980                if self.peek().text.eq_ignore_ascii_case("OPTIMIZE")
981                    && matches!(
982                        self.config.dialect,
983                        Some(crate::dialects::DialectType::ClickHouse)
984                    ) =>
985            {
986                self.skip(); // consume OPTIMIZE
987                self.parse_command()?
988                    .ok_or_else(|| self.parse_error("Failed to parse OPTIMIZE statement"))
989            }
990            // ClickHouse: EXISTS [TEMPORARY] TABLE/DATABASE/DICTIONARY ...
991            TokenType::Exists
992                if matches!(
993                    self.config.dialect,
994                    Some(crate::dialects::DialectType::ClickHouse)
995                ) && !self.check_next(TokenType::LParen) =>
996            {
997                self.skip(); // consume EXISTS
998                self.parse_command()?
999                    .ok_or_else(|| self.parse_error("Failed to parse EXISTS statement"))
1000            }
1001            // ClickHouse: SHOW ... (various SHOW commands beyond what's already handled)
1002            TokenType::Var
1003                if self.peek().text.eq_ignore_ascii_case("EXISTS")
1004                    && matches!(
1005                        self.config.dialect,
1006                        Some(crate::dialects::DialectType::ClickHouse)
1007                    ) =>
1008            {
1009                self.skip(); // consume EXISTS
1010                self.parse_command()?
1011                    .ok_or_else(|| self.parse_error("Failed to parse EXISTS statement"))
1012            }
1013            // DuckDB: ATTACH [DATABASE] [IF NOT EXISTS] 'path' [AS alias] [(options)]
1014            TokenType::Var if self.peek().text.eq_ignore_ascii_case("ATTACH") => {
1015                self.skip(); // consume ATTACH
1016                if matches!(
1017                    self.config.dialect,
1018                    Some(crate::dialects::DialectType::ClickHouse)
1019                ) {
1020                    self.parse_command()?
1021                        .ok_or_else(|| self.parse_error("Failed to parse ATTACH statement"))
1022                } else {
1023                    self.parse_attach_detach(true)
1024                }
1025            }
1026            // ClickHouse: UNDROP TABLE [IF EXISTS] ... [UUID '...'] [ON CLUSTER ...]
1027            TokenType::Var
1028                if self.peek().text.eq_ignore_ascii_case("UNDROP")
1029                    && matches!(
1030                        self.config.dialect,
1031                        Some(crate::dialects::DialectType::ClickHouse)
1032                    ) =>
1033            {
1034                self.skip(); // consume UNDROP
1035                self.parse_command()?
1036                    .ok_or_else(|| self.parse_error("Failed to parse UNDROP statement"))
1037            }
1038            // ClickHouse: DETACH TABLE [IF EXISTS] ... [ON CLUSTER ...]
1039            TokenType::Var
1040                if self.peek().text.eq_ignore_ascii_case("DETACH")
1041                    && matches!(
1042                        self.config.dialect,
1043                        Some(crate::dialects::DialectType::ClickHouse)
1044                    ) =>
1045            {
1046                self.skip(); // consume DETACH
1047                self.parse_command()?
1048                    .ok_or_else(|| self.parse_error("Failed to parse DETACH statement"))
1049            }
1050            // DuckDB: DETACH [DATABASE] [IF EXISTS] name
1051            TokenType::Var if self.peek().text.eq_ignore_ascii_case("DETACH") => {
1052                self.skip(); // consume DETACH
1053                self.parse_attach_detach(false)
1054            }
1055            // DuckDB: INSTALL extension [FROM source]
1056            TokenType::Var if self.peek().text.eq_ignore_ascii_case("INSTALL") => {
1057                self.skip(); // consume INSTALL
1058                self.parse_install(false)
1059            }
1060            // DuckDB: FORCE INSTALL extension | FORCE CHECKPOINT db
1061            TokenType::Var if self.peek().text.eq_ignore_ascii_case("FORCE") => {
1062                self.skip(); // consume FORCE
1063                self.parse_force_statement()
1064            }
1065            // DuckDB: SUMMARIZE [TABLE] expression
1066            TokenType::Var if self.peek().text.eq_ignore_ascii_case("SUMMARIZE") => {
1067                self.skip(); // consume SUMMARIZE
1068                self.parse_summarize_statement()
1069            }
1070            // DuckDB: RESET [SESSION|GLOBAL|LOCAL] variable
1071            TokenType::Var if self.peek().text.eq_ignore_ascii_case("RESET") => {
1072                self.skip(); // consume RESET
1073                self.parse_as_command()?
1074                    .ok_or_else(|| self.parse_error("Failed to parse RESET statement"))
1075            }
1076            // DuckDB statement-level PIVOT/UNPIVOT/PIVOT_WIDER syntax
1077            TokenType::Pivot => {
1078                self.skip(); // consume PIVOT
1079                self.parse_simplified_pivot(false)?
1080                    .ok_or_else(|| self.parse_error("Failed to parse PIVOT statement"))
1081            }
1082            TokenType::Unpivot => {
1083                self.skip(); // consume UNPIVOT
1084                self.parse_simplified_pivot(true)?
1085                    .ok_or_else(|| self.parse_error("Failed to parse UNPIVOT statement"))
1086            }
1087            // DuckDB: PIVOT_WIDER is an alias for PIVOT
1088            TokenType::Var if self.peek().text.eq_ignore_ascii_case("PIVOT_WIDER") => {
1089                self.skip(); // consume PIVOT_WIDER
1090                self.parse_simplified_pivot(false)?
1091                    .ok_or_else(|| self.parse_error("Failed to parse PIVOT_WIDER statement"))
1092            }
1093            // BigQuery procedural FOR...IN...DO loop
1094            TokenType::For => {
1095                self.skip(); // consume FOR
1096                self.parse_for_in()
1097            }
1098            // BigQuery/procedural LOOP, REPEAT, WHILE control flow statements
1099            TokenType::Var if self.peek().text.eq_ignore_ascii_case("LOOP") => {
1100                self.skip(); // consume LOOP
1101                self.parse_command()?
1102                    .ok_or_else(|| self.parse_error("Failed to parse LOOP statement"))
1103            }
1104            TokenType::Var if self.peek().text.eq_ignore_ascii_case("REPEAT") => {
1105                self.skip(); // consume REPEAT
1106                self.parse_command()?
1107                    .ok_or_else(|| self.parse_error("Failed to parse REPEAT statement"))
1108            }
1109            TokenType::Var if self.peek().text.eq_ignore_ascii_case("WHILE") => {
1110                self.skip(); // consume WHILE
1111                self.parse_command()?
1112                    .ok_or_else(|| self.parse_error("Failed to parse WHILE statement"))
1113            }
1114            // Athena/Presto: UNLOAD (SELECT ...) TO 'location' WITH (options)
1115            TokenType::Var if self.peek().text.eq_ignore_ascii_case("UNLOAD") => {
1116                self.parse_unload()
1117            }
1118            // Athena: USING EXTERNAL FUNCTION ... SELECT ...
1119            TokenType::Using => self.parse_using_external_function(),
1120            // BigQuery: EXPORT DATA [WITH CONNECTION conn] OPTIONS (...) AS SELECT ...
1121            TokenType::Var if self.peek().text.eq_ignore_ascii_case("EXPORT") => {
1122                self.parse_export_data()
1123            }
1124            // Presto/Trino: DEALLOCATE PREPARE <name>
1125            TokenType::Var if self.peek().text.eq_ignore_ascii_case("DEALLOCATE") => {
1126                self.parse_deallocate_prepare()
1127            }
1128            // DuckDB FROM-first syntax: FROM tbl = SELECT * FROM tbl
1129            TokenType::From => self.parse_from_first_query(),
1130            TokenType::LParen => {
1131                // Check if this is a parenthesized query (SELECT, WITH, PIVOT, UNPIVOT, FROM, or EXPLAIN inside)
1132                // by looking ahead after the opening paren
1133                let next_is_explain = self.current + 1 < self.tokens.len()
1134                    && self.tokens[self.current + 1].token_type == TokenType::Var
1135                    && self.tokens[self.current + 1]
1136                        .text
1137                        .eq_ignore_ascii_case("EXPLAIN");
1138                if self.check_next(TokenType::Select)
1139                    || self.check_next(TokenType::With)
1140                    || self.check_next(TokenType::Pivot)
1141                    || self.check_next(TokenType::Unpivot)
1142                    || self.check_next(TokenType::From)
1143                    || next_is_explain
1144                {
1145                    // Parse parenthesized query: (SELECT ...) ORDER BY x LIMIT y OFFSET z
1146                    self.skip(); // consume (
1147                    let inner = self.parse_statement()?;
1148                    self.expect(TokenType::RParen)?;
1149                    // Wrap in Subquery to preserve parentheses when used in set operations
1150                    let subquery = Expression::Subquery(Box::new(Subquery {
1151                        this: inner,
1152                        alias: None,
1153                        column_aliases: Vec::new(),
1154                        order_by: None,
1155                        limit: None,
1156                        offset: None,
1157                        distribute_by: None,
1158                        sort_by: None,
1159                        cluster_by: None,
1160                        lateral: false,
1161                        modifiers_inside: false,
1162                        trailing_comments: Vec::new(),
1163                        inferred_type: None,
1164                    }));
1165                    // Check for set operations after the parenthesized query
1166                    let result = self.parse_set_operation(subquery)?;
1167                    // Check for ORDER BY, LIMIT, OFFSET after parenthesized subquery
1168                    self.parse_query_modifiers(result)
1169                } else if self.check_next(TokenType::LParen) {
1170                    // Nested parentheses - could be ((SELECT...)) or ((a, b))
1171                    // For deeply nested queries like (((SELECT 1) UNION SELECT 1) UNION SELECT 1),
1172                    // recurse into parse_statement to handle the inner parenthesized query with set ops
1173                    self.skip(); // consume (
1174                    let inner = self.parse_statement()?;
1175                    // Check for set operations inside the outer parens
1176                    let result = self.parse_set_operation(inner)?;
1177                    self.expect(TokenType::RParen)?;
1178                    let subquery = Expression::Subquery(Box::new(Subquery {
1179                        this: result,
1180                        alias: None,
1181                        column_aliases: Vec::new(),
1182                        order_by: None,
1183                        limit: None,
1184                        offset: None,
1185                        distribute_by: None,
1186                        sort_by: None,
1187                        cluster_by: None,
1188                        lateral: false,
1189                        modifiers_inside: false,
1190                        trailing_comments: Vec::new(),
1191                        inferred_type: None,
1192                    }));
1193                    // Check for set operations after the outer parenthesized query
1194                    let result = self.parse_set_operation(subquery)?;
1195                    let pre_alias_comments = self.previous_trailing_comments().to_vec();
1196                    if self.match_token(TokenType::As) {
1197                        let alias = self.expect_identifier_or_keyword_with_quoted()?;
1198                        let trailing_comments = self.previous_trailing_comments().to_vec();
1199                        Ok(Expression::Alias(Box::new(Alias {
1200                            this: result,
1201                            alias,
1202                            column_aliases: Vec::new(),
1203                            pre_alias_comments,
1204                            trailing_comments,
1205                            inferred_type: None,
1206                        })))
1207                    } else {
1208                        // Check for LIMIT/OFFSET after parenthesized expression
1209                        // e.g., ((SELECT 1)) LIMIT 1
1210                        self.parse_query_modifiers(result)
1211                    }
1212                } else {
1213                    // Regular parenthesized expression like (a, b) or (x)
1214                    // Let parse_expression handle it
1215                    let expr = self.parse_expression()?;
1216                    let pre_alias_comments = self.previous_trailing_comments().to_vec();
1217                    if self.match_token(TokenType::As) {
1218                        // Check for tuple alias: AS ("a", "b", ...)
1219                        if self.match_token(TokenType::LParen) {
1220                            let mut column_aliases = Vec::new();
1221                            loop {
1222                                let col_alias = self.expect_identifier_or_keyword_with_quoted()?;
1223                                column_aliases.push(col_alias);
1224                                if !self.match_token(TokenType::Comma) {
1225                                    break;
1226                                }
1227                            }
1228                            self.expect(TokenType::RParen)?;
1229                            let trailing_comments = self.previous_trailing_comments().to_vec();
1230                            Ok(Expression::Alias(Box::new(Alias {
1231                                this: expr,
1232                                alias: Identifier::empty(),
1233                                column_aliases,
1234                                pre_alias_comments,
1235                                trailing_comments,
1236                                inferred_type: None,
1237                            })))
1238                        } else {
1239                            let alias = self.expect_identifier_or_keyword_with_quoted()?;
1240                            let trailing_comments = self.previous_trailing_comments().to_vec();
1241                            Ok(Expression::Alias(Box::new(Alias {
1242                                this: expr,
1243                                alias,
1244                                column_aliases: Vec::new(),
1245                                pre_alias_comments,
1246                                trailing_comments,
1247                                inferred_type: None,
1248                            })))
1249                        }
1250                    } else {
1251                        Ok(expr)
1252                    }
1253                }
1254            }
1255            _ => {
1256                // Capture leading comments from the first token before parsing
1257                let leading_comments = self.current_leading_comments().to_vec();
1258                // Parse expression and check for optional alias
1259                let expr = self.parse_expression()?;
1260                // Capture any comments between expression and AS keyword
1261                let pre_alias_comments = self.previous_trailing_comments().to_vec();
1262                if self.match_token(TokenType::As) {
1263                    // Capture comments from AS token (e.g., AS /* foo */ (a, b, c))
1264                    // These go into trailing_comments (after the alias), not pre_alias_comments
1265                    let as_comments = self.previous_trailing_comments().to_vec();
1266                    // Check for tuple alias: AS ("a", "b", ...)
1267                    if self.match_token(TokenType::LParen) {
1268                        let mut column_aliases = Vec::new();
1269                        loop {
1270                            let col_alias = self.expect_identifier_or_keyword_with_quoted()?;
1271                            column_aliases.push(col_alias);
1272                            if !self.match_token(TokenType::Comma) {
1273                                break;
1274                            }
1275                        }
1276                        self.expect(TokenType::RParen)?;
1277                        let mut trailing_comments = as_comments;
1278                        trailing_comments.extend_from_slice(self.previous_trailing_comments());
1279                        Ok(Expression::Alias(Box::new(Alias {
1280                            this: expr,
1281                            alias: Identifier::empty(),
1282                            column_aliases,
1283                            pre_alias_comments,
1284                            trailing_comments,
1285                            inferred_type: None,
1286                        })))
1287                    } else {
1288                        let alias = self.expect_identifier_or_keyword_with_quoted()?;
1289                        let mut trailing_comments = self.previous_trailing_comments().to_vec();
1290                        // If there were leading comments on the expression (from a separate line),
1291                        // add them as trailing comments after the alias
1292                        trailing_comments.extend(leading_comments.iter().cloned());
1293                        Ok(Expression::Alias(Box::new(Alias {
1294                            this: expr,
1295                            alias,
1296                            column_aliases: Vec::new(),
1297                            pre_alias_comments,
1298                            trailing_comments,
1299                            inferred_type: None,
1300                        })))
1301                    }
1302                } else if (self.check(TokenType::Var) && !self.check_keyword())
1303                    || self.is_command_keyword_as_alias()
1304                {
1305                    // Implicit alias (without AS) - e.g., "1. x" or "1.x" -> "1. AS x"
1306                    // This handles cases like PostgreSQL's "1.x" which parses as float 1. with alias x
1307                    let alias_text = self.advance().text.clone();
1308                    let trailing_comments = self.previous_trailing_comments().to_vec();
1309                    Ok(Expression::Alias(Box::new(Alias {
1310                        this: expr,
1311                        alias: Identifier::new(alias_text),
1312                        column_aliases: Vec::new(),
1313                        pre_alias_comments,
1314                        trailing_comments,
1315                        inferred_type: None,
1316                    })))
1317                } else if !pre_alias_comments.is_empty() {
1318                    // Wrap in Annotated to preserve trailing comments for expressions without aliases
1319                    match &expr {
1320                        Expression::Literal(_) | Expression::Boolean(_) | Expression::Null(_) => {
1321                            Ok(Expression::Annotated(Box::new(
1322                                crate::expressions::Annotated {
1323                                    this: expr,
1324                                    trailing_comments: pre_alias_comments,
1325                                },
1326                            )))
1327                        }
1328                        // For expressions that already have trailing_comments fields, don't double-wrap
1329                        _ => Ok(expr),
1330                    }
1331                } else if !leading_comments.is_empty() {
1332                    // Wrap in Annotated to preserve leading comments as trailing comments
1333                    // This matches Python sqlglot which converts leading line comments to trailing block comments
1334                    Ok(Expression::Annotated(Box::new(
1335                        crate::expressions::Annotated {
1336                            this: expr,
1337                            trailing_comments: leading_comments,
1338                        },
1339                    )))
1340                } else {
1341                    Ok(expr)
1342                }
1343            }
1344        }
1345    }
1346
1347    /// Parse a SELECT statement
1348    fn parse_select(&mut self) -> Result<Expression> {
1349        // Capture the SELECT token to get its comments
1350        let select_token = self.expect(TokenType::Select)?;
1351        let leading_comments = select_token.comments;
1352        let post_select_comments = select_token.trailing_comments;
1353
1354        // Parse query hint /*+ ... */ if present (comes immediately after SELECT)
1355        let hint = if self.check(TokenType::Hint) {
1356            Some(self.parse_hint()?)
1357        } else {
1358            None
1359        };
1360
1361        // Parse TOP clause (SQL Server style - comes before DISTINCT)
1362        // But not if TOP is followed by DOT (e.g., SELECT top.x - top is a table alias)
1363        let top = if self.check(TokenType::Top)
1364            && !self.check_next(TokenType::Dot)
1365            && self.match_token(TokenType::Top)
1366        {
1367            // TOP can have parentheses: TOP (10) or without: TOP 10
1368            let (amount, parenthesized) = if self.match_token(TokenType::LParen) {
1369                let expr = self.parse_expression()?;
1370                self.expect(TokenType::RParen)?;
1371                (expr, true)
1372            } else {
1373                (self.parse_primary()?, false)
1374            };
1375            let percent = self.match_token(TokenType::Percent);
1376            let with_ties = self.match_keywords(&[TokenType::With, TokenType::Ties]);
1377            Some(Top {
1378                this: amount,
1379                percent,
1380                with_ties,
1381                parenthesized,
1382            })
1383        } else {
1384            None
1385        };
1386
1387        // Parse DISTINCT / DISTINCT ON / DISTINCTROW / ALL
1388        // Oracle: UNIQUE is equivalent to DISTINCT (SELECT UNIQUE ... is old-style Oracle syntax)
1389        let is_distinct_token = self.match_token(TokenType::Distinct)
1390            || (matches!(
1391                self.config.dialect,
1392                Some(crate::dialects::DialectType::Oracle)
1393            ) && self.match_token(TokenType::Unique));
1394        let (distinct, distinct_on) = if is_distinct_token {
1395            if self.match_token(TokenType::On) {
1396                // DISTINCT ON (expr, ...)
1397                self.expect(TokenType::LParen)?;
1398                let exprs = self.parse_expression_list()?;
1399                self.expect(TokenType::RParen)?;
1400                (true, Some(exprs))
1401            } else {
1402                (true, None)
1403            }
1404        } else if self.check_identifier("DISTINCTROW") {
1405            // MySQL DISTINCTROW - equivalent to DISTINCT
1406            self.skip();
1407            (true, None)
1408        } else {
1409            // Only consume ALL if it's the SELECT ALL modifier, not if it's a column reference like "all.count"
1410            if self.check(TokenType::All) && !self.check_next(TokenType::Dot) {
1411                self.skip();
1412            }
1413            (false, None)
1414        };
1415
1416        // TSQL: SELECT DISTINCT TOP n - TOP can come after DISTINCT
1417        // If no TOP was parsed before DISTINCT, check for TOP after DISTINCT
1418        let top = if top.is_none()
1419            && self.check(TokenType::Top)
1420            && !self.check_next(TokenType::Dot)
1421            && self.match_token(TokenType::Top)
1422        {
1423            let (amount, parenthesized) = if self.match_token(TokenType::LParen) {
1424                let expr = self.parse_expression()?;
1425                self.expect(TokenType::RParen)?;
1426                (expr, true)
1427            } else {
1428                (self.parse_primary()?, false)
1429            };
1430            let percent = self.match_token(TokenType::Percent);
1431            let with_ties = self.match_keywords(&[TokenType::With, TokenType::Ties]);
1432            Some(Top {
1433                this: amount,
1434                percent,
1435                with_ties,
1436                parenthesized,
1437            })
1438        } else {
1439            top
1440        };
1441
1442        // Parse MySQL operation modifiers (HIGH_PRIORITY, STRAIGHT_JOIN, SQL_CALC_FOUND_ROWS, etc.)
1443        // These appear after DISTINCT/ALL and before the projections
1444        // Only apply for MySQL-family dialects - other dialects treat these as identifiers
1445        let mut operation_modifiers = Vec::new();
1446        let is_mysql_dialect = matches!(
1447            self.config.dialect,
1448            Some(crate::dialects::DialectType::MySQL)
1449                | Some(crate::dialects::DialectType::SingleStore)
1450                | Some(crate::dialects::DialectType::StarRocks)
1451                | Some(crate::dialects::DialectType::TiDB)
1452                | Some(crate::dialects::DialectType::Doris)
1453        );
1454        if is_mysql_dialect {
1455            const MYSQL_MODIFIERS: &[&str] = &[
1456                "HIGH_PRIORITY",
1457                "STRAIGHT_JOIN",
1458                "SQL_SMALL_RESULT",
1459                "SQL_BIG_RESULT",
1460                "SQL_BUFFER_RESULT",
1461                "SQL_NO_CACHE",
1462                "SQL_CALC_FOUND_ROWS",
1463            ];
1464            loop {
1465                if self.check(TokenType::StraightJoin) {
1466                    self.skip();
1467                    operation_modifiers.push("STRAIGHT_JOIN".to_string());
1468                } else if self.check(TokenType::Var) {
1469                    let upper = self.peek().text.to_ascii_uppercase();
1470                    if MYSQL_MODIFIERS.contains(&upper.as_str()) {
1471                        self.skip();
1472                        operation_modifiers.push(upper);
1473                    } else {
1474                        break;
1475                    }
1476                } else {
1477                    break;
1478                }
1479            }
1480        }
1481
1482        // Parse BigQuery SELECT AS STRUCT / SELECT AS VALUE
1483        let kind = if self.match_token(TokenType::As) {
1484            if self.match_identifier("STRUCT") {
1485                Some("STRUCT".to_string())
1486            } else if self.match_identifier("VALUE") {
1487                Some("VALUE".to_string())
1488            } else {
1489                // Not AS STRUCT/VALUE, backtrack the AS token
1490                self.current -= 1;
1491                None
1492            }
1493        } else {
1494            None
1495        };
1496
1497        // Parse select expressions
1498        let mut expressions = self.parse_select_expressions()?;
1499
1500        // Redshift: EXCLUDE clause at the end of the projection list
1501        // e.g., SELECT *, 4 AS col4 EXCLUDE (col2, col3) FROM ...
1502        // e.g., SELECT col1, *, col2 EXCLUDE(col3) FROM ...
1503        // e.g., SELECT *, 4 AS col4 EXCLUDE col2, col3 FROM ...
1504        // In Python sqlglot, this is handled by overriding _parse_projections in the Redshift parser.
1505        // The EXCLUDE clause is separate from * EXCLUDE — it applies to the entire projection list.
1506        let exclude = if matches!(
1507            self.config.dialect,
1508            Some(crate::dialects::DialectType::Redshift)
1509        ) {
1510            // Check if previous token was EXCLUDE (parsed as implicit alias).
1511            // e.g., SELECT *, 4 AS col4 EXCLUDE col2, col3 FROM ...
1512            //   → "col4 EXCLUDE" was parsed as (col4 aliased-as EXCLUDE), then "col2" as next projection
1513            //   → We need to strip the EXCLUDE alias from the last projection and retreat
1514            // Also handle: EXCLUDE was consumed as a bare column name if no AS was present
1515            let mut retreat_for_exclude = false;
1516            if let Some(last_expr) = expressions.last() {
1517                // Case: "4 AS col4 EXCLUDE" without parens — parsed as separate column "EXCLUDE"
1518                // Actually with the comma break, this won't happen. But "col2 EXCLUDE(col3)" might.
1519                match last_expr {
1520                    Expression::Alias(alias) if alias.alias.name.eq_ignore_ascii_case("EXCLUDE") => {
1521                        // The last expression is "something AS EXCLUDE" or implicit alias EXCLUDE
1522                        // Strip the alias and check if EXCLUDE is followed by paren or identifier
1523                        if self.check(TokenType::LParen) || self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
1524                            // Strip the EXCLUDE alias from the last expression
1525                            let stripped = alias.this.clone();
1526                            if let Some(last) = expressions.last_mut() {
1527                                *last = stripped;
1528                            }
1529                            retreat_for_exclude = true;
1530                        }
1531                    }
1532                    _ => {}
1533                }
1534            }
1535
1536            if retreat_for_exclude || self.check(TokenType::Exclude) {
1537                if !retreat_for_exclude {
1538                    self.skip(); // consume EXCLUDE
1539                }
1540                // Parse EXCLUDE columns - with or without parens
1541                let mut exclude_cols = Vec::new();
1542                if self.match_token(TokenType::LParen) {
1543                    // Parenthesized list: EXCLUDE (col1, col2, ...)
1544                    loop {
1545                        let col_expr = self.parse_expression()?;
1546                        exclude_cols.push(col_expr);
1547                        if !self.match_token(TokenType::Comma) {
1548                            break;
1549                        }
1550                    }
1551                    self.match_token(TokenType::RParen);
1552                } else {
1553                    // Non-parenthesized: EXCLUDE col1, col2, ...
1554                    // Parse comma-separated identifiers until FROM or other clause boundary
1555                    loop {
1556                        if self.is_at_end() || self.check(TokenType::From) || self.check(TokenType::Where)
1557                            || self.check(TokenType::Semicolon) || self.check(TokenType::RParen)
1558                        {
1559                            break;
1560                        }
1561                        let col_expr = self.parse_expression()?;
1562                        exclude_cols.push(col_expr);
1563                        if !self.match_token(TokenType::Comma) {
1564                            break;
1565                        }
1566                    }
1567                }
1568                if exclude_cols.is_empty() { None } else { Some(exclude_cols) }
1569            } else {
1570                None
1571            }
1572        } else {
1573            None
1574        };
1575
1576        // Parse INTO clause (SELECT ... INTO [TEMPORARY|UNLOGGED] table_name)
1577        // Also handles Oracle PL/SQL: BULK COLLECT INTO v1, v2, ...
1578        let into = if self.match_text_seq(&["BULK", "COLLECT", "INTO"]) {
1579            // Oracle PL/SQL: BULK COLLECT INTO var1, var2, ...
1580            // Parse target variables as a comma-separated list
1581            let mut target_expressions = vec![self.parse_expression()?];
1582            while self.match_token(TokenType::Comma) {
1583                target_expressions.push(self.parse_expression()?);
1584            }
1585            if target_expressions.len() == 1 {
1586                Some(SelectInto {
1587                    this: target_expressions.remove(0),
1588                    temporary: false,
1589                    unlogged: false,
1590                    bulk_collect: true,
1591                    expressions: Vec::new(),
1592                })
1593            } else {
1594                // Multiple targets - use first as `this` and rest as `expressions`
1595                // Actually, to match Python sqlglot behavior, store all in expressions
1596                Some(SelectInto {
1597                    this: Expression::Null(Null),
1598                    temporary: false,
1599                    unlogged: false,
1600                    bulk_collect: true,
1601                    expressions: target_expressions,
1602                })
1603            }
1604        } else if self.match_token(TokenType::Into) {
1605            // Check for TEMPORARY/TEMP/UNLOGGED keyword (PostgreSQL)
1606            let temporary = self.match_token(TokenType::Temporary) || self.match_identifier("TEMP");
1607            let unlogged = !temporary && self.match_identifier("UNLOGGED");
1608            // Parse first target (table name or PL/SQL variable)
1609            let table_name = self.parse_table_ref()?;
1610            // Oracle PL/SQL: SELECT ... INTO var1, var2, ... FROM ...
1611            // If followed by comma, parse additional target variables
1612            if self.match_token(TokenType::Comma) {
1613                let mut target_expressions = vec![Expression::Table(Box::new(table_name))];
1614                target_expressions.push(self.parse_expression()?);
1615                while self.match_token(TokenType::Comma) {
1616                    target_expressions.push(self.parse_expression()?);
1617                }
1618                Some(SelectInto {
1619                    this: Expression::Null(Null),
1620                    temporary,
1621                    unlogged,
1622                    bulk_collect: false,
1623                    expressions: target_expressions,
1624                })
1625            } else {
1626                Some(SelectInto {
1627                    this: Expression::Table(Box::new(table_name)),
1628                    temporary,
1629                    unlogged,
1630                    bulk_collect: false,
1631                    expressions: Vec::new(),
1632                })
1633            }
1634        } else {
1635            None
1636        };
1637
1638        // Parse FROM clause
1639        let from = if self.match_token(TokenType::From) {
1640            Some(self.parse_from()?)
1641        } else {
1642            None
1643        };
1644
1645        // Parse JOINs
1646        let mut joins = self.parse_joins()?;
1647
1648        // Handle PIVOT/UNPIVOT that comes after JOINs (e.g., SELECT * FROM a JOIN b ON ... PIVOT(...))
1649        // Store PIVOT/UNPIVOT in the last join's pivots field (this matches SQLGlot's semantics)
1650        while self.check(TokenType::Pivot) || self.check(TokenType::Unpivot) {
1651            if !joins.is_empty() {
1652                let last_idx = joins.len() - 1;
1653                // Parse the pivot/unpivot and store in the join's pivots vector
1654                // We pass a Null expression as the `this` since the pivot applies to the entire join result
1655                if self.match_token(TokenType::Pivot) {
1656                    let pivot = self.parse_pivot(Expression::Null(crate::expressions::Null))?;
1657                    joins[last_idx].pivots.push(pivot);
1658                } else if self.match_token(TokenType::Unpivot) {
1659                    let unpivot = self.parse_unpivot(Expression::Null(crate::expressions::Null))?;
1660                    joins[last_idx].pivots.push(unpivot);
1661                }
1662            } else {
1663                // No joins - break to avoid infinite loop
1664                break;
1665            }
1666        }
1667
1668        // Parse LATERAL VIEW clauses (Hive/Spark)
1669        let lateral_views = self.parse_lateral_views()?;
1670
1671        // Parse PREWHERE clause (ClickHouse specific)
1672        let prewhere = if self.match_token(TokenType::Prewhere) {
1673            Some(self.parse_expression()?)
1674        } else {
1675            None
1676        };
1677
1678        // Parse WHERE clause
1679        let mut where_clause = if self.match_token(TokenType::Where) {
1680            Some(Where {
1681                this: self.parse_expression()?,
1682            })
1683        } else {
1684            None
1685        };
1686
1687        // Parse CONNECT BY clause (Oracle hierarchical queries)
1688        let connect = self.parse_connect()?;
1689
1690        // Parse GROUP BY
1691        let group_by = if self.check(TokenType::Group) {
1692            let group_comments = self.current_leading_comments().to_vec();
1693            if self.match_keywords(&[TokenType::Group, TokenType::By]) {
1694                let mut gb = self.parse_group_by()?;
1695                gb.comments = group_comments;
1696                Some(gb)
1697            } else {
1698                None
1699            }
1700        } else if matches!(
1701            self.config.dialect,
1702            Some(crate::dialects::DialectType::ClickHouse)
1703        ) && self.check(TokenType::With)
1704            && (self.check_next_identifier("TOTALS")
1705                || self.check_next(TokenType::Rollup)
1706                || self.check_next(TokenType::Cube))
1707        {
1708            // ClickHouse: WITH TOTALS/ROLLUP/CUBE without GROUP BY
1709            self.skip(); // consume WITH
1710            let totals = self.match_identifier("TOTALS");
1711            let mut expressions = Vec::new();
1712            if self.match_token(TokenType::Rollup) {
1713                expressions.push(Expression::Rollup(Box::new(Rollup {
1714                    expressions: Vec::new(),
1715                })));
1716            } else if self.match_token(TokenType::Cube) {
1717                expressions.push(Expression::Cube(Box::new(Cube {
1718                    expressions: Vec::new(),
1719                })));
1720            }
1721            // Check for chained WITH TOTALS after WITH ROLLUP/CUBE
1722            if !totals && self.check(TokenType::With) && self.check_next_identifier("TOTALS") {
1723                self.skip();
1724                self.skip();
1725            }
1726            Some(GroupBy {
1727                expressions,
1728                all: None,
1729                totals,
1730                comments: Vec::new(),
1731            })
1732        } else {
1733            None
1734        };
1735
1736        // Parse HAVING
1737        let having = if self.check(TokenType::Having) {
1738            let having_comments = self.current_leading_comments().to_vec();
1739            self.skip(); // consume HAVING
1740            Some(Having {
1741                this: self.parse_expression()?,
1742                comments: having_comments,
1743            })
1744        } else {
1745            None
1746        };
1747
1748        // Parse QUALIFY clause (Snowflake, BigQuery, DuckDB)
1749        // QUALIFY can appear before or after WINDOW clause
1750        let mut qualify = if self.match_token(TokenType::Qualify) {
1751            Some(Qualify {
1752                this: self.parse_expression()?,
1753            })
1754        } else {
1755            None
1756        };
1757
1758        // Parse WINDOW clause (named windows)
1759        // Only match WINDOW if followed by identifier AS ( (a real window definition)
1760        // Otherwise "window" may be a table alias (e.g., SELECT * FROM foo window)
1761        let windows = if self.check(TokenType::Window) && {
1762            let next_pos = self.current + 1;
1763            next_pos < self.tokens.len()
1764                && (self.tokens[next_pos].token_type == TokenType::Var
1765                    || self.tokens[next_pos].token_type == TokenType::Identifier)
1766        } {
1767            self.skip(); // consume WINDOW
1768            Some(self.parse_named_windows()?)
1769        } else {
1770            None
1771        };
1772
1773        // QUALIFY can also appear after WINDOW clause (DuckDB)
1774        let qualify_after_window = if qualify.is_none() && self.match_token(TokenType::Qualify) {
1775            qualify = Some(Qualify {
1776                this: self.parse_expression()?,
1777            });
1778            true
1779        } else {
1780            false
1781        };
1782
1783        // Parse DISTRIBUTE BY (Hive/Spark) - comes before SORT BY
1784        let distribute_by = if self.match_keywords(&[TokenType::Distribute, TokenType::By]) {
1785            Some(self.parse_distribute_by()?)
1786        } else {
1787            None
1788        };
1789
1790        // Parse CLUSTER BY (Hive/Spark)
1791        let cluster_by = if self.match_keywords(&[TokenType::Cluster, TokenType::By]) {
1792            Some(self.parse_cluster_by()?)
1793        } else {
1794            None
1795        };
1796
1797        // Parse SORT BY (Hive/Spark) - can come before ORDER BY
1798        let sort_by = if self.match_keywords(&[TokenType::Sort, TokenType::By]) {
1799            Some(self.parse_sort_by()?)
1800        } else {
1801            None
1802        };
1803
1804        // Parse ORDER BY or ORDER SIBLINGS BY (Oracle) - comes after SORT BY
1805        let order_by = if self.check(TokenType::Order) {
1806            let order_comments = self.current_leading_comments().to_vec();
1807            if self.match_keywords(&[TokenType::Order, TokenType::Siblings, TokenType::By]) {
1808                // ORDER SIBLINGS BY (Oracle hierarchical queries)
1809                let mut ob = self.parse_order_by_with_siblings(true)?;
1810                ob.comments = order_comments;
1811                Some(ob)
1812            } else if self.match_keywords(&[TokenType::Order, TokenType::By]) {
1813                let mut ob = self.parse_order_by()?;
1814                ob.comments = order_comments;
1815                Some(ob)
1816            } else {
1817                None
1818            }
1819        } else {
1820            None
1821        };
1822
1823        // Parse LIMIT (supports MySQL syntax: LIMIT offset, count)
1824        // DuckDB supports: LIMIT 10 PERCENT or LIMIT 10%
1825        // Capture trailing comments from the token before LIMIT (e.g., WHERE condition's last token)
1826        // These comments should be emitted after the LIMIT value, not before LIMIT.
1827        let pre_limit_comments = if self.check(TokenType::Limit) {
1828            let mut comments = self.previous_trailing_comments().to_vec();
1829            // Also capture leading comments on the LIMIT token (comments on a separate line before LIMIT)
1830            comments.extend_from_slice(self.current_leading_comments());
1831            comments
1832        } else {
1833            Vec::new()
1834        };
1835        let (limit, offset) = if self.match_token(TokenType::Limit) {
1836            // Clear the pre-LIMIT comments from the WHERE condition expression to avoid duplication
1837            if !pre_limit_comments.is_empty() {
1838                if let Some(ref mut w) = where_clause {
1839                    Self::clear_rightmost_trailing_comments(&mut w.this);
1840                }
1841            }
1842            // First try parse_unary to check for PERCENT/% modifier.
1843            // This avoids parse_expression consuming % as the modulo operator.
1844            // Both "PERCENT" and "%" tokens have TokenType::Percent, but we need to
1845            // distinguish PERCENT-as-modifier from %-as-modulo. "%" is PERCENT when
1846            // followed by a clause boundary (OFFSET, end, semicolon, etc.).
1847            let saved_pos = self.current;
1848            let (first_expr, has_percent) = {
1849                let unary_result = self.parse_unary();
1850                match unary_result {
1851                    Ok(expr) => {
1852                        if self.check(TokenType::Percent) && self.is_percent_modifier() {
1853                            // Found PERCENT keyword or % symbol used as PERCENT modifier
1854                            self.skip();
1855                            (expr, true)
1856                        } else {
1857                            // No PERCENT - backtrack and use full parse_expression
1858                            self.current = saved_pos;
1859                            let full_expr = self.parse_expression()?;
1860                            // Check again for PERCENT keyword (e.g., after complex expression)
1861                            let has_pct =
1862                                if self.check(TokenType::Percent) && self.is_percent_modifier() {
1863                                    self.skip();
1864                                    true
1865                                } else {
1866                                    false
1867                                };
1868                            (full_expr, has_pct)
1869                        }
1870                    }
1871                    Err(_) => {
1872                        // Unary parsing failed - backtrack and use parse_expression
1873                        self.current = saved_pos;
1874                        let full_expr = self.parse_expression()?;
1875                        let has_pct =
1876                            if self.check(TokenType::Percent) && self.is_percent_modifier() {
1877                                self.skip();
1878                                true
1879                            } else {
1880                                false
1881                            };
1882                        (full_expr, has_pct)
1883                    }
1884                }
1885            };
1886            // MySQL syntax: LIMIT offset, count
1887            if self.match_token(TokenType::Comma) {
1888                let second_expr = self.parse_expression()?;
1889                // First expression is offset, second is count
1890                (
1891                    Some(Limit {
1892                        this: second_expr,
1893                        percent: false,
1894                        comments: pre_limit_comments.clone(),
1895                    }),
1896                    Some(Offset {
1897                        this: first_expr,
1898                        rows: None,
1899                    }),
1900                )
1901            } else {
1902                // Standard: LIMIT count [PERCENT]
1903                (
1904                    Some(Limit {
1905                        this: first_expr,
1906                        percent: has_percent,
1907                        comments: pre_limit_comments,
1908                    }),
1909                    None,
1910                )
1911            }
1912        } else {
1913            (None, None)
1914        };
1915
1916        // WITH TIES after LIMIT (ClickHouse, DuckDB)
1917        if limit.is_some() {
1918            let _ = self.match_keywords(&[TokenType::With, TokenType::Ties]);
1919        }
1920
1921        // Parse OFFSET (if not already parsed from MySQL LIMIT syntax)
1922        // Standard SQL syntax: OFFSET n [ROW|ROWS]
1923        // Some dialects (Presto/Trino) support: OFFSET n LIMIT m
1924        let (limit, offset) = if offset.is_none() && self.match_token(TokenType::Offset) {
1925            let expr = self.parse_expression()?;
1926            // Consume optional ROW or ROWS keyword and track it
1927            let rows = if self.match_token(TokenType::Row) || self.match_token(TokenType::Rows) {
1928                Some(true)
1929            } else {
1930                None
1931            };
1932            let offset = Some(Offset { this: expr, rows });
1933
1934            // Check for LIMIT after OFFSET (Presto/Trino syntax: OFFSET n LIMIT m)
1935            let limit = if limit.is_none() && self.match_token(TokenType::Limit) {
1936                let limit_expr = self.parse_expression()?;
1937                Some(Limit {
1938                    this: limit_expr,
1939                    percent: false,
1940                    comments: Vec::new(),
1941                })
1942            } else {
1943                limit
1944            };
1945
1946            (limit, offset)
1947        } else {
1948            (limit, offset)
1949        };
1950
1951        // ClickHouse: LIMIT ... BY expressions
1952        let limit_by = if matches!(
1953            self.config.dialect,
1954            Some(crate::dialects::DialectType::ClickHouse)
1955        ) && limit.is_some()
1956            && self.match_token(TokenType::By)
1957        {
1958            let expressions = self.parse_expression_list()?;
1959            if expressions.is_empty() {
1960                return Err(self.parse_error("Expected expression after LIMIT BY"));
1961            }
1962            Some(expressions)
1963        } else {
1964            None
1965        };
1966
1967        // ClickHouse: second LIMIT after LIMIT BY (LIMIT n BY expr LIMIT m)
1968        // Also supports LIMIT offset, count syntax
1969        let (limit, offset) = if limit_by.is_some() && self.match_token(TokenType::Limit) {
1970            let first_expr = self.parse_expression()?;
1971            if self.match_token(TokenType::Comma) {
1972                // LIMIT offset, count
1973                let count_expr = self.parse_expression()?;
1974                (
1975                    Some(Limit {
1976                        this: count_expr,
1977                        percent: false,
1978                        comments: Vec::new(),
1979                    }),
1980                    Some(Offset {
1981                        this: first_expr,
1982                        rows: None,
1983                    }),
1984                )
1985            } else {
1986                (
1987                    Some(Limit {
1988                        this: first_expr,
1989                        percent: false,
1990                        comments: Vec::new(),
1991                    }),
1992                    offset,
1993                )
1994            }
1995        } else {
1996            (limit, offset)
1997        };
1998
1999        // Parse FETCH FIRST/NEXT clause
2000        let fetch = if self.match_token(TokenType::Fetch) {
2001            Some(self.parse_fetch()?)
2002        } else {
2003            None
2004        };
2005
2006        // Parse SAMPLE / TABLESAMPLE clause
2007        let sample = self.parse_sample_clause()?;
2008
2009        // Parse FOR UPDATE/SHARE locks or FOR XML (T-SQL)
2010        let (locks, for_xml) = self.parse_locks_and_for_xml()?;
2011
2012        // TSQL: OPTION clause (e.g., OPTION(LABEL = 'foo', HASH JOIN))
2013        let option = if self.check_identifier("OPTION") && self.check_next(TokenType::LParen) {
2014            self.skip(); // consume OPTION
2015            self.skip(); // consume (
2016            let mut content = String::from("OPTION(");
2017            let mut depth = 1;
2018            while !self.is_at_end() && depth > 0 {
2019                let tok = self.advance();
2020                if tok.token_type == TokenType::LParen {
2021                    depth += 1;
2022                } else if tok.token_type == TokenType::RParen {
2023                    depth -= 1;
2024                }
2025                if depth > 0 {
2026                    if tok.token_type == TokenType::String {
2027                        if content.len() > 7 && !content.ends_with('(') && !content.ends_with(' ') {
2028                            content.push(' ');
2029                        }
2030                        content.push('\'');
2031                        content.push_str(&tok.text.replace('\'', "''"));
2032                        content.push('\'');
2033                    } else if tok.token_type == TokenType::Eq {
2034                        content.push_str(" = ");
2035                    } else if tok.token_type == TokenType::Comma {
2036                        content.push_str(", ");
2037                    } else {
2038                        if content.len() > 7 && !content.ends_with('(') && !content.ends_with(' ') {
2039                            content.push(' ');
2040                        }
2041                        content.push_str(&tok.text);
2042                    }
2043                }
2044            }
2045            content.push(')');
2046            Some(content)
2047        } else {
2048            None
2049        };
2050
2051        // ClickHouse: SETTINGS and FORMAT clauses after LIMIT/OFFSET/FETCH
2052        let (settings, format) = if matches!(
2053            self.config.dialect,
2054            Some(crate::dialects::DialectType::ClickHouse)
2055        ) {
2056            let mut settings: Option<Vec<Expression>> = None;
2057            let mut format: Option<Expression> = None;
2058
2059            loop {
2060                if settings.is_none() && self.match_token(TokenType::Settings) {
2061                    let mut settings_exprs = Vec::new();
2062                    loop {
2063                        settings_exprs.push(self.parse_expression()?);
2064                        if !self.match_token(TokenType::Comma) {
2065                            break;
2066                        }
2067                    }
2068                    settings = Some(settings_exprs);
2069                    continue;
2070                }
2071
2072                if format.is_none() && self.match_token(TokenType::Format) {
2073                    // ClickHouse: FORMAT Null is valid (Null is a keyword token, not an identifier)
2074                    let ident = if self.check(TokenType::Null) {
2075                        let text = self.advance().text;
2076                        Identifier::new(text)
2077                    } else {
2078                        self.expect_identifier_or_keyword_with_quoted()?
2079                    };
2080                    format = Some(Expression::Identifier(ident));
2081                    // ClickHouse: FORMAT <name> may be followed by inline data
2082                    // (CSV rows, JSON objects, etc.) — consume to semicolon
2083                    if matches!(
2084                        self.config.dialect,
2085                        Some(crate::dialects::DialectType::ClickHouse)
2086                    ) && !self.is_at_end()
2087                        && !self.check(TokenType::Semicolon)
2088                        && !self.check(TokenType::Settings)
2089                    {
2090                        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
2091                            self.skip();
2092                        }
2093                    }
2094                    continue;
2095                }
2096
2097                break;
2098            }
2099
2100            (settings, format)
2101        } else {
2102            (None, None)
2103        };
2104
2105        let select = Select {
2106            expressions,
2107            from,
2108            joins,
2109            lateral_views,
2110            prewhere,
2111            where_clause,
2112            group_by,
2113            having,
2114            qualify,
2115            order_by,
2116            distribute_by,
2117            cluster_by,
2118            sort_by,
2119            limit,
2120            offset,
2121            limit_by,
2122            fetch,
2123            distinct,
2124            distinct_on,
2125            top,
2126            with: None,
2127            sample,
2128            settings,
2129            format,
2130            windows,
2131            hint,
2132            connect,
2133            into,
2134            locks,
2135            for_xml,
2136            leading_comments,
2137            post_select_comments,
2138            kind,
2139            operation_modifiers,
2140            qualify_after_window,
2141            option,
2142            exclude,
2143        };
2144
2145        // Check for set operations (UNION, INTERSECT, EXCEPT)
2146        let result = Expression::Select(Box::new(select));
2147        self.parse_set_operation(result)
2148    }
2149
2150    /// Parse a WITH clause (CTEs)
2151    fn parse_with(&mut self) -> Result<Expression> {
2152        use crate::dialects::DialectType;
2153
2154        let with_token = self.expect(TokenType::With)?;
2155        let leading_comments = with_token.comments;
2156
2157        let recursive = self.match_token(TokenType::Recursive);
2158        let mut ctes = Vec::new();
2159
2160        loop {
2161            // ClickHouse supports expression-first WITH items:
2162            // WITH <expr> AS <alias> SELECT ...
2163            if matches!(self.config.dialect, Some(DialectType::ClickHouse)) {
2164                let saved_pos = self.current;
2165                if let Ok(expr) = self.parse_expression() {
2166                    // Check if parse_expression already consumed the AS alias
2167                    // (e.g., `(1, 2) AS a` gets parsed as Alias(Tuple, "a") by the tuple alias handler)
2168                    let (inner_expr, alias_opt) = if let Expression::Alias(ref alias_box) = expr {
2169                        (alias_box.this.clone(), Some(alias_box.alias.clone()))
2170                    } else {
2171                        (expr, None)
2172                    };
2173
2174                    if let Some(alias) = alias_opt {
2175                        // Expression already had AS alias consumed
2176                        ctes.push(Cte {
2177                            alias,
2178                            this: inner_expr,
2179                            columns: Vec::new(),
2180                            materialized: None,
2181                            key_expressions: Vec::new(),
2182                            alias_first: false,
2183                            comments: Vec::new(),
2184                        });
2185
2186                        if self.match_token(TokenType::Comma) {
2187                            continue;
2188                        }
2189                        break;
2190                    } else if self.match_token(TokenType::As)
2191                        && self.is_identifier_or_keyword_token()
2192                    {
2193                        // Require AS <alias> to disambiguate from standard CTE syntax
2194                        let alias = self.expect_identifier_or_keyword_with_quoted()?;
2195                        ctes.push(Cte {
2196                            alias,
2197                            this: inner_expr,
2198                            columns: Vec::new(),
2199                            materialized: None,
2200                            key_expressions: Vec::new(),
2201                            alias_first: false,
2202                            comments: Vec::new(),
2203                        });
2204
2205                        if self.match_token(TokenType::Comma) {
2206                            continue;
2207                        }
2208                        break;
2209                    } else if self.check(TokenType::Select) || self.check(TokenType::Comma) {
2210                        // ClickHouse: WITH expr SELECT ... (unaliased expression in CTE)
2211                        ctes.push(Cte {
2212                            alias: Identifier::new(format!("{}", inner_expr)),
2213                            this: inner_expr,
2214                            columns: Vec::new(),
2215                            materialized: None,
2216                            key_expressions: Vec::new(),
2217                            alias_first: false,
2218                            comments: Vec::new(),
2219                        });
2220
2221                        if self.match_token(TokenType::Comma) {
2222                            continue;
2223                        }
2224                        break;
2225                    }
2226                }
2227                // Fall back to standard CTE parsing
2228                self.current = saved_pos;
2229            }
2230
2231            // CTE names can be keywords like 'view', 'use', 'all', etc.
2232            let name = self.expect_identifier_or_alias_keyword_with_quoted()?;
2233
2234            // Optional column list
2235            // But first check for Snowflake-style CTE: WITH t (SELECT ...) - no AS keyword
2236            // In that case, LParen is followed by SELECT, not column names
2237            let columns = if self.check(TokenType::LParen) && !self.check_next(TokenType::Select) {
2238                self.skip(); // consume LParen
2239                let cols = self.parse_identifier_list()?;
2240                self.expect(TokenType::RParen)?;
2241                cols
2242            } else {
2243                Vec::new()
2244            };
2245
2246            // Optional USING KEY (columns) for DuckDB recursive CTEs
2247            let key_expressions = if self.match_keywords(&[TokenType::Using, TokenType::Key]) {
2248                self.expect(TokenType::LParen)?;
2249                let keys = self.parse_identifier_list()?;
2250                self.expect(TokenType::RParen)?;
2251                keys
2252            } else {
2253                Vec::new()
2254            };
2255
2256            // ClickHouse: keyword -> body AS alias (single-param lambda where param is a keyword)
2257            // e.g., WITH time -> sin(time * 2 * pi()) AS sine_wave
2258            if matches!(self.config.dialect, Some(DialectType::ClickHouse))
2259                && self.check(TokenType::Arrow)
2260            {
2261                self.skip(); // consume ->
2262                let body = self.parse_expression()?;
2263                let lambda = Expression::Lambda(Box::new(LambdaExpr {
2264                    parameters: vec![name.clone()],
2265                    body,
2266                    colon: false,
2267                    parameter_types: Vec::new(),
2268                }));
2269                // Expect AS alias
2270                if self.match_token(TokenType::As) && self.is_identifier_or_keyword_token() {
2271                    let alias = self.expect_identifier_or_keyword_with_quoted()?;
2272                    ctes.push(Cte {
2273                        alias,
2274                        this: lambda,
2275                        columns: Vec::new(),
2276                        materialized: None,
2277                        key_expressions: Vec::new(),
2278                        alias_first: false,
2279                        comments: Vec::new(),
2280                    });
2281                } else {
2282                    // Unaliased lambda CTE
2283                    ctes.push(Cte {
2284                        alias: name,
2285                        this: lambda,
2286                        columns: Vec::new(),
2287                        materialized: None,
2288                        key_expressions: Vec::new(),
2289                        alias_first: false,
2290                        comments: Vec::new(),
2291                    });
2292                }
2293                if self.match_token(TokenType::Comma) {
2294                    continue;
2295                }
2296                break;
2297            }
2298
2299            // AS is optional (Snowflake allows WITH t (SELECT ...) without AS)
2300            let cte_comments = if self.match_token(TokenType::As) {
2301                // Capture trailing comments from the AS token
2302                // e.g., "WITH a AS /* comment */ (...)" -> comment goes after alias
2303                self.previous_trailing_comments().to_vec()
2304            } else {
2305                Vec::new()
2306            };
2307
2308            // Check for MATERIALIZED or NOT MATERIALIZED
2309            let materialized = if self.match_token(TokenType::Materialized) {
2310                Some(true)
2311            } else if self.match_token(TokenType::Not) {
2312                self.expect(TokenType::Materialized)?;
2313                Some(false)
2314            } else {
2315                None
2316            };
2317
2318            self.expect(TokenType::LParen)?;
2319            let query = self.parse_statement()?;
2320            self.expect(TokenType::RParen)?;
2321
2322            ctes.push(Cte {
2323                alias: name,
2324                this: query,
2325                columns,
2326                materialized,
2327                key_expressions,
2328                alias_first: true,
2329                comments: cte_comments,
2330            });
2331
2332            if !self.match_token(TokenType::Comma) {
2333                // Check for WITH merging: WITH a AS (...) WITH b AS (...) -> merged
2334                // If the next token is WITH (not followed by nothing), continue parsing CTEs
2335                if self.check(TokenType::With) {
2336                    self.skip(); // consume the redundant WITH keyword
2337                                    // Check if this WITH is also RECURSIVE
2338                    if self.match_token(TokenType::Recursive) && !recursive {
2339                        // If second WITH is RECURSIVE but first wasn't, ignore (keep non-recursive)
2340                    }
2341                    continue; // continue the loop to parse more CTEs
2342                }
2343                break;
2344            }
2345            // WI-14f: Skip redundant WITH keyword after comma in CTE list
2346            // e.g., WITH a AS (SELECT 1), WITH b AS (SELECT 2) SELECT *
2347            self.match_token(TokenType::With);
2348        }
2349
2350        // Parse optional SEARCH/CYCLE clause for recursive CTEs (PostgreSQL)
2351        // Syntax: SEARCH BREADTH|DEPTH FIRST BY column SET column [USING column]
2352        //     or: CYCLE column SET column USING column
2353        let search = self.parse_recursive_with_search()?;
2354
2355        // Parse the main query
2356        let mut main_query = self.parse_statement()?;
2357
2358        // Unwrap parenthesized wrappers to find the inner SELECT
2359        // (matching Python sqlglot: while isinstance(this, Subquery) and this.is_wrapper)
2360        loop {
2361            match main_query {
2362                Expression::Paren(paren) => {
2363                    main_query = paren.this;
2364                }
2365                Expression::Subquery(ref sub)
2366                    if sub.alias.is_none()
2367                        && sub.order_by.is_none()
2368                        && sub.limit.is_none()
2369                        && sub.offset.is_none() =>
2370                {
2371                    // Unwrap Subquery wrapper (parenthesized query without modifiers)
2372                    if let Expression::Subquery(sub) = main_query {
2373                        main_query = sub.this;
2374                    } else {
2375                        break;
2376                    }
2377                }
2378                _ => break,
2379            }
2380        }
2381
2382        // Attach WITH to the main query
2383        let with_clause = With {
2384            ctes,
2385            recursive,
2386            leading_comments,
2387            search,
2388        };
2389        match &mut main_query {
2390            Expression::Select(ref mut select) => {
2391                select.with = Some(with_clause);
2392            }
2393            Expression::Union(ref mut union) => {
2394                union.with = Some(with_clause);
2395            }
2396            Expression::Intersect(ref mut intersect) => {
2397                intersect.with = Some(with_clause);
2398            }
2399            Expression::Except(ref mut except) => {
2400                except.with = Some(with_clause);
2401            }
2402            Expression::Update(ref mut update) => {
2403                update.with = Some(with_clause);
2404            }
2405            Expression::Insert(ref mut insert) => {
2406                insert.with = Some(with_clause);
2407            }
2408            Expression::Delete(ref mut delete) => {
2409                delete.with = Some(with_clause);
2410            }
2411            Expression::CreateTable(ref mut ct) => {
2412                ct.with_cte = Some(with_clause);
2413            }
2414            Expression::Pivot(ref mut pivot) => {
2415                pivot.with = Some(with_clause);
2416            }
2417            _ => {}
2418        }
2419
2420        Ok(main_query)
2421    }
2422
2423    /// Parse SELECT expressions
2424    fn parse_select_expressions(&mut self) -> Result<Vec<Expression>> {
2425        let mut expressions = Vec::new();
2426
2427        loop {
2428            // Check if we're at end of select list (empty list case for TSQL TOP)
2429            // This allows queries like "SELECT TOP 10 PERCENT" with no columns
2430            // Also check for Oracle BULK COLLECT INTO sequence
2431            // ClickHouse: minus() is tokenized as Except but should be treated as function
2432            let is_ch_keyword_func = matches!(
2433                self.config.dialect,
2434                Some(crate::dialects::DialectType::ClickHouse)
2435            ) && (self.check(TokenType::Except)
2436                || self.check(TokenType::Intersect))
2437                && self.check_next(TokenType::LParen);
2438            // ClickHouse: `from`/`except` can be column names when followed by an operator
2439            // (e.g., `from + from`, `from in [0]`, `from, ...`)
2440            // Also: `from FROM t` — two consecutive FROM tokens means first is column name
2441            let is_ch_keyword_as_column = matches!(
2442                self.config.dialect,
2443                Some(crate::dialects::DialectType::ClickHouse)
2444            ) && (self.check(TokenType::From)
2445                || self.check(TokenType::Except))
2446                && {
2447                    let next_tt = self
2448                        .peek_nth(1)
2449                        .map(|t| t.token_type)
2450                        .unwrap_or(TokenType::Semicolon);
2451                    matches!(
2452                        next_tt,
2453                        TokenType::Plus | TokenType::Dash | TokenType::Star | TokenType::Slash
2454                        | TokenType::Percent | TokenType::Eq | TokenType::Neq | TokenType::Lt
2455                        | TokenType::Gt | TokenType::Lte | TokenType::Gte
2456                        | TokenType::And | TokenType::Or | TokenType::Comma | TokenType::Dot
2457                        | TokenType::In | TokenType::Is | TokenType::Not | TokenType::Like
2458                        | TokenType::Between | TokenType::Semicolon | TokenType::RParen
2459                        | TokenType::As | TokenType::DPipe | TokenType::Amp | TokenType::Pipe
2460                        | TokenType::LBracket
2461                        // Two consecutive FROM tokens: first is column name (e.g., SELECT from FROM t)
2462                        | TokenType::From
2463                    )
2464                };
2465            if !is_ch_keyword_func
2466                && !is_ch_keyword_as_column
2467                && (self.is_at_end()
2468                    || self.check(TokenType::From)
2469                    || self.check(TokenType::Where)
2470                    || self.check(TokenType::Into)
2471                    || self.check(TokenType::Union)
2472                    || self.check(TokenType::Intersect)
2473                    || self.check(TokenType::Except)
2474                    || self.check(TokenType::Order)
2475                    || self.check(TokenType::Limit)
2476                    || self.check(TokenType::Semicolon)
2477                    || self.check_text_seq(&["BULK", "COLLECT", "INTO"]))
2478            {
2479                break;
2480            }
2481
2482            // Handle star
2483            if self.check(TokenType::Star) {
2484                self.skip();
2485                let star_trailing_comments = self.previous_trailing_comments().to_vec();
2486                let star = self.parse_star_modifiers_with_comments(None, star_trailing_comments)?;
2487                let mut star_expr = Expression::Star(star);
2488                // ClickHouse: * APPLY(func) or * APPLY func or * APPLY(x -> expr) column transformer
2489                if matches!(
2490                    self.config.dialect,
2491                    Some(crate::dialects::DialectType::ClickHouse)
2492                ) {
2493                    while self.check(TokenType::Apply) {
2494                        self.skip(); // consume APPLY
2495                        let apply_expr = if self.match_token(TokenType::LParen) {
2496                            // Could be APPLY(func_name) or APPLY(x -> expr)
2497                            let expr = self.parse_expression()?;
2498                            self.expect(TokenType::RParen)?;
2499                            expr
2500                        } else {
2501                            // APPLY func or APPLY x -> expr (no parens)
2502                            // Parse as expression to handle lambdas
2503                            self.parse_expression()?
2504                        };
2505                        star_expr = Expression::Apply(Box::new(crate::expressions::Apply {
2506                            this: Box::new(star_expr),
2507                            expression: Box::new(apply_expr),
2508                        }));
2509                    }
2510                }
2511                // ClickHouse: Also handle EXCEPT/REPLACE between APPLYs:
2512                // * APPLY(toDate) EXCEPT(i, j) APPLY(any)
2513                if matches!(
2514                    self.config.dialect,
2515                    Some(crate::dialects::DialectType::ClickHouse)
2516                ) && (self.check(TokenType::Except)
2517                    || self.check(TokenType::Exclude)
2518                    || self.check(TokenType::Replace))
2519                {
2520                    // Consume EXCEPT/REPLACE modifiers after APPLY
2521                    self.parse_star_modifiers(None)?;
2522                    // Continue with more APPLYs
2523                    while self.check(TokenType::Apply) {
2524                        self.skip();
2525                        let apply_expr = if self.match_token(TokenType::LParen) {
2526                            let expr = self.parse_expression()?;
2527                            self.expect(TokenType::RParen)?;
2528                            expr
2529                        } else {
2530                            self.parse_expression()?
2531                        };
2532                        star_expr = Expression::Apply(Box::new(crate::expressions::Apply {
2533                            this: Box::new(star_expr),
2534                            expression: Box::new(apply_expr),
2535                        }));
2536                    }
2537                }
2538                // ClickHouse: * followed by operators (e.g., * IS NOT NULL, * AND expr)
2539                // Treat * as a regular expression and continue parsing operators
2540                if matches!(
2541                    self.config.dialect,
2542                    Some(crate::dialects::DialectType::ClickHouse)
2543                ) && matches!(
2544                    self.peek().token_type,
2545                    TokenType::Is
2546                        | TokenType::And
2547                        | TokenType::Or
2548                        | TokenType::Eq
2549                        | TokenType::Neq
2550                        | TokenType::Lt
2551                        | TokenType::Gt
2552                        | TokenType::Lte
2553                        | TokenType::Gte
2554                        | TokenType::Not
2555                        | TokenType::Plus
2556                        | TokenType::Dash
2557                        | TokenType::Slash
2558                        | TokenType::Percent
2559                        | TokenType::Like
2560                        | TokenType::Between
2561                        | TokenType::In
2562                ) {
2563                    // Re-parse from the operator with star_expr as the left side
2564                    let left = star_expr;
2565                    // Use parse_comparison / parse_is chain
2566                    if self.check(TokenType::Is) {
2567                        self.skip(); // consume IS
2568                        let not = self.match_token(TokenType::Not);
2569                        if self.match_token(TokenType::Null) {
2570                            star_expr = if not {
2571                                Expression::Not(Box::new(UnaryOp {
2572                                    this: Expression::Is(Box::new(BinaryOp::new(
2573                                        left,
2574                                        Expression::Null(Null),
2575                                    ))),
2576                                    inferred_type: None,
2577                                }))
2578                            } else {
2579                                Expression::Is(Box::new(BinaryOp::new(
2580                                    left,
2581                                    Expression::Null(Null),
2582                                )))
2583                            };
2584                        } else {
2585                            let right = self.parse_or()?;
2586                            star_expr = if not {
2587                                Expression::Not(Box::new(UnaryOp {
2588                                    this: Expression::Is(Box::new(BinaryOp::new(left, right))),
2589                                    inferred_type: None,
2590                                }))
2591                            } else {
2592                                Expression::Is(Box::new(BinaryOp::new(left, right)))
2593                            };
2594                        }
2595                    } else if self.match_token(TokenType::And) {
2596                        let right = self.parse_or()?;
2597                        star_expr = Expression::And(Box::new(BinaryOp::new(left, right)));
2598                    } else if self.match_token(TokenType::Or) {
2599                        let right = self.parse_or()?;
2600                        star_expr = Expression::Or(Box::new(BinaryOp::new(left, right)));
2601                    } else {
2602                        let op_token = self.advance();
2603                        let right = self.parse_or()?;
2604                        star_expr = match op_token.token_type {
2605                            TokenType::Eq => Expression::Eq(Box::new(BinaryOp::new(left, right))),
2606                            TokenType::Neq => Expression::Neq(Box::new(BinaryOp::new(left, right))),
2607                            TokenType::Lt => Expression::Lt(Box::new(BinaryOp::new(left, right))),
2608                            TokenType::Gt => Expression::Gt(Box::new(BinaryOp::new(left, right))),
2609                            TokenType::Lte => Expression::Lte(Box::new(BinaryOp::new(left, right))),
2610                            TokenType::Gte => Expression::Gte(Box::new(BinaryOp::new(left, right))),
2611                            TokenType::Plus => {
2612                                Expression::Add(Box::new(BinaryOp::new(left, right)))
2613                            }
2614                            TokenType::Dash => {
2615                                Expression::Sub(Box::new(BinaryOp::new(left, right)))
2616                            }
2617                            _ => left, // fallback
2618                        };
2619                    }
2620                }
2621                expressions.push(star_expr);
2622            } else {
2623                // Capture leading comments from the first token before parsing
2624                // These are comments on a separate line before the expression
2625                let leading_comments = self.current_leading_comments().to_vec();
2626                let expr = self.parse_expression()?;
2627
2628                // ClickHouse: COLUMNS(id, value) EXCEPT (id) REPLACE (5 AS id) APPLY func
2629                // Also: a.* APPLY(toDate) EXCEPT(i, j) APPLY(any) - qualified star with APPLY
2630                let expr = if matches!(
2631                    self.config.dialect,
2632                    Some(crate::dialects::DialectType::ClickHouse)
2633                ) {
2634                    let is_columns_func = match &expr {
2635                        Expression::Function(f) => f.name.eq_ignore_ascii_case("COLUMNS"),
2636                        Expression::MethodCall(m) => m.method.name.eq_ignore_ascii_case("COLUMNS"),
2637                        Expression::Columns(_) => true,
2638                        _ => false,
2639                    };
2640                    let is_qualified_star = matches!(&expr, Expression::Star(_));
2641                    if (is_columns_func || is_qualified_star)
2642                        && (self.check(TokenType::Except)
2643                            || self.check(TokenType::Exclude)
2644                            || self.check(TokenType::Replace)
2645                            || self.check(TokenType::Apply))
2646                    {
2647                        let mut result = expr;
2648                        // Parse any mix of EXCEPT/REPLACE/APPLY in any order
2649                        // e.g., * APPLY(toDate) EXCEPT(i, j) APPLY(any)
2650                        loop {
2651                            if self.check(TokenType::Except) || self.check(TokenType::Exclude) {
2652                                // Parse EXCEPT/EXCLUDE modifier
2653                                self.skip();
2654                                self.match_identifier("STRICT");
2655                                if self.match_token(TokenType::LParen) {
2656                                    loop {
2657                                        if self.check(TokenType::RParen) {
2658                                            break;
2659                                        }
2660                                        let _ = self.parse_expression()?;
2661                                        if !self.match_token(TokenType::Comma) {
2662                                            break;
2663                                        }
2664                                    }
2665                                    self.expect(TokenType::RParen)?;
2666                                } else if self.is_identifier_token()
2667                                    || self.is_safe_keyword_as_identifier()
2668                                {
2669                                    let _ = self.parse_expression()?;
2670                                }
2671                            } else if self.check(TokenType::Replace) {
2672                                // Parse REPLACE modifier: REPLACE (expr AS alias, ...)
2673                                self.skip();
2674                                self.match_identifier("STRICT");
2675                                if self.match_token(TokenType::LParen) {
2676                                    loop {
2677                                        if self.check(TokenType::RParen) {
2678                                            break;
2679                                        }
2680                                        let _ = self.parse_expression()?;
2681                                        if self.match_token(TokenType::As) {
2682                                            if self.is_identifier_token()
2683                                                || self.is_safe_keyword_as_identifier()
2684                                            {
2685                                                self.skip();
2686                                            }
2687                                        }
2688                                        if !self.match_token(TokenType::Comma) {
2689                                            break;
2690                                        }
2691                                    }
2692                                    self.expect(TokenType::RParen)?;
2693                                } else {
2694                                    let _ = self.parse_expression()?;
2695                                    if self.match_token(TokenType::As) {
2696                                        if self.is_identifier_token()
2697                                            || self.is_safe_keyword_as_identifier()
2698                                        {
2699                                            self.skip();
2700                                        }
2701                                    }
2702                                }
2703                            } else if self.check(TokenType::Apply) {
2704                                // Parse APPLY transformer
2705                                self.skip();
2706                                let apply_expr = if self.match_token(TokenType::LParen) {
2707                                    let e = self.parse_expression()?;
2708                                    self.expect(TokenType::RParen)?;
2709                                    e
2710                                } else {
2711                                    self.parse_expression()?
2712                                };
2713                                result = Expression::Apply(Box::new(crate::expressions::Apply {
2714                                    this: Box::new(result),
2715                                    expression: Box::new(apply_expr),
2716                                }));
2717                            } else {
2718                                break;
2719                            }
2720                        }
2721                        result
2722                    } else {
2723                        expr
2724                    }
2725                } else {
2726                    expr
2727                };
2728
2729                // Capture comments between expression and potential AS
2730                let pre_alias_comments = self.previous_trailing_comments().to_vec();
2731
2732                // DuckDB prefix alias syntax: identifier: expression (e.g., "foo: 1" means "1 AS foo")
2733                // Check if the expression is a simple identifier followed by a colon
2734                let expr = if self.check(TokenType::Colon) && !self.check_next(TokenType::Colon) {
2735                    // Extract the alias name from the identifier expression
2736                    let alias_ident = match &expr {
2737                        Expression::Identifier(id) => Some(id.clone()),
2738                        Expression::Column(col) if col.table.is_none() => Some(col.name.clone()),
2739                        _ => None,
2740                    };
2741                    if let Some(alias) = alias_ident {
2742                        // Consume the colon
2743                        self.skip();
2744                        let colon_comments = self.previous_trailing_comments().to_vec();
2745                        // Parse the actual value expression
2746                        let value = self.parse_expression()?;
2747                        let value_trailing = self.previous_trailing_comments().to_vec();
2748                        // For colon-alias (foo: expr), comments between alias and colon should
2749                        // become trailing comments (placed after the alias in output).
2750                        // Comments after the value expression are also trailing.
2751                        let mut all_trailing = pre_alias_comments.clone();
2752                        all_trailing.extend(colon_comments);
2753                        all_trailing.extend(value_trailing);
2754                        Expression::Alias(Box::new(Alias {
2755                            this: value,
2756                            alias,
2757                            column_aliases: Vec::new(),
2758                            pre_alias_comments: Vec::new(),
2759                            trailing_comments: all_trailing,
2760                            inferred_type: None,
2761                        }))
2762                    } else {
2763                        // Not a simple identifier, fall through to normal alias handling
2764                        // (this handles cases where the expression is complex before the colon)
2765                        expr
2766                    }
2767                } else if self.match_token(TokenType::As) {
2768                    // Capture comments from AS token (e.g., AS /* foo */ (a, b, c))
2769                    // These go into trailing_comments (after the alias), not pre_alias_comments
2770                    let as_comments = self.previous_trailing_comments().to_vec();
2771                    // Check for column aliases: AS (col1, col2) - used by POSEXPLODE etc.
2772                    if self.match_token(TokenType::LParen) {
2773                        let mut column_aliases = Vec::new();
2774                        loop {
2775                            if let Some(col_expr) = self.parse_id_var()? {
2776                                if let Expression::Identifier(id) = col_expr {
2777                                    column_aliases.push(id);
2778                                }
2779                            } else {
2780                                break;
2781                            }
2782                            if !self.match_token(TokenType::Comma) {
2783                                break;
2784                            }
2785                        }
2786                        self.match_token(TokenType::RParen);
2787                        let mut trailing_comments = as_comments;
2788                        trailing_comments.extend_from_slice(self.previous_trailing_comments());
2789                        Expression::Alias(Box::new(Alias {
2790                            this: expr,
2791                            alias: Identifier::new(String::new()),
2792                            column_aliases,
2793                            pre_alias_comments,
2794                            trailing_comments,
2795                            inferred_type: None,
2796                        }))
2797                    } else {
2798                        // Allow keywords as aliases (e.g., SELECT 1 AS filter)
2799                        // Use _with_quoted to preserve quoted alias
2800                        let alias = self.expect_identifier_or_keyword_with_quoted()?;
2801                        let mut trailing_comments = self.previous_trailing_comments().to_vec();
2802                        // If parse_comparison stored pending leading comments (no comparison
2803                        // followed), use those. Otherwise use the leading_comments we captured
2804                        // before parse_expression(). Both come from the same token, so we
2805                        // only add one set to avoid duplication.
2806                        if !self.pending_leading_comments.is_empty() {
2807                            trailing_comments.extend(self.pending_leading_comments.drain(..));
2808                        } else {
2809                            trailing_comments.extend(leading_comments.iter().cloned());
2810                        }
2811                        Expression::Alias(Box::new(Alias {
2812                            this: expr,
2813                            alias,
2814                            column_aliases: Vec::new(),
2815                            pre_alias_comments,
2816                            trailing_comments,
2817                            inferred_type: None,
2818                        }))
2819                    }
2820                } 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)
2821                    // ClickHouse: APPLY without ( is an implicit alias (e.g., SELECT col apply)
2822                    || (self.check(TokenType::Apply) && !self.check_next(TokenType::LParen)
2823                        && matches!(self.config.dialect, Some(crate::dialects::DialectType::ClickHouse))))
2824                    && !self.check_text_seq(&["BULK", "COLLECT", "INTO"])
2825                    // ClickHouse clauses must not be consumed as implicit aliases.
2826                    && !(matches!(self.config.dialect, Some(crate::dialects::DialectType::ClickHouse))
2827                        && (self.check(TokenType::Format) || self.check(TokenType::Settings)))
2828                    // LIMIT/OFFSET/FETCH are clause starters in most dialects and must not
2829                    // be consumed as implicit aliases in SELECT lists.
2830                    && !(
2831                        self.check(TokenType::Fetch)
2832                        || ((self.check(TokenType::Limit) || self.check(TokenType::Offset))
2833                            && !matches!(
2834                                self.config.dialect,
2835                                Some(crate::dialects::DialectType::Spark)
2836                                    | Some(crate::dialects::DialectType::Hive)
2837                            ))
2838                    )
2839                    // GROUP BY / ORDER BY are clause boundaries, not aliases.
2840                    && !self.check_text_seq(&["GROUP", "BY"])
2841                    && !self.check_text_seq(&["ORDER", "BY"])
2842                    // WINDOW is a clause boundary (named window definitions), not an alias.
2843                    && !self.check(TokenType::Window)
2844                    // ClickHouse: PARALLEL WITH is a statement separator, not an alias.
2845                    && !(self.check_identifier("PARALLEL") && self.check_next(TokenType::With)
2846                        && matches!(self.config.dialect, Some(crate::dialects::DialectType::ClickHouse)))
2847                {
2848                    // Implicit alias (without AS) - allow Var tokens, QuotedIdentifiers, command keywords (like GET, PUT, etc.), and OVERLAPS
2849                    // But NOT when it's the Oracle BULK COLLECT INTO sequence
2850                    let alias_token = self.advance();
2851                    let alias_text = alias_token.text.clone();
2852                    let is_quoted = alias_token.token_type == TokenType::QuotedIdentifier;
2853                    let trailing_comments = self.previous_trailing_comments().to_vec();
2854                    Expression::Alias(Box::new(Alias {
2855                        this: expr,
2856                        alias: Identifier {
2857                            name: alias_text,
2858                            quoted: is_quoted,
2859                            trailing_comments: Vec::new(),
2860                            span: None,
2861                        },
2862                        column_aliases: Vec::new(),
2863                        pre_alias_comments,
2864                        trailing_comments,
2865                        inferred_type: None,
2866                    }))
2867                } else if !pre_alias_comments.is_empty() {
2868                    // Only wrap in Annotated if the expression doesn't already handle trailing comments.
2869                    // BinaryOp, Column, Cast, Function, etc. have their own trailing_comments field that the generator uses.
2870                    let already_has_trailing = matches!(
2871                        &expr,
2872                        Expression::Add(_)
2873                            | Expression::Sub(_)
2874                            | Expression::Mul(_)
2875                            | Expression::Div(_)
2876                            | Expression::Mod(_)
2877                            | Expression::Concat(_)
2878                            | Expression::BitwiseAnd(_)
2879                            | Expression::BitwiseOr(_)
2880                            | Expression::BitwiseXor(_)
2881                            | Expression::Column(_)
2882                            | Expression::Paren(_)
2883                            | Expression::Annotated(_)
2884                            | Expression::Cast(_)
2885                            | Expression::Function(_)
2886                            | Expression::Subquery(_)
2887                    );
2888                    if already_has_trailing {
2889                        expr
2890                    } else {
2891                        // Wrap in Annotated to preserve trailing comments
2892                        Expression::Annotated(Box::new(Annotated {
2893                            this: expr,
2894                            trailing_comments: pre_alias_comments,
2895                        }))
2896                    }
2897                } else if !leading_comments.is_empty() {
2898                    // Wrap in Annotated to preserve leading comments as trailing comments
2899                    Expression::Annotated(Box::new(Annotated {
2900                        this: expr,
2901                        trailing_comments: leading_comments,
2902                    }))
2903                } else {
2904                    expr
2905                };
2906
2907                expressions.push(expr);
2908            }
2909
2910            if !self.match_token(TokenType::Comma) {
2911                break;
2912            }
2913
2914            // Handle trailing comma (ClickHouse supports trailing commas in SELECT)
2915            // ClickHouse: `from` after comma is a column name if followed by an operator
2916            // (e.g., `from + from` or `from in [0]`), comma, or line-end
2917            let from_is_column = matches!(
2918                self.config.dialect,
2919                Some(crate::dialects::DialectType::ClickHouse)
2920            ) && self.check(TokenType::From)
2921                && {
2922                    let next_tt = self
2923                        .peek_nth(1)
2924                        .map(|t| t.token_type)
2925                        .unwrap_or(TokenType::Semicolon);
2926                    matches!(
2927                        next_tt,
2928                        TokenType::Plus
2929                            | TokenType::Dash
2930                            | TokenType::Star
2931                            | TokenType::Slash
2932                            | TokenType::Percent
2933                            | TokenType::Eq
2934                            | TokenType::Neq
2935                            | TokenType::Lt
2936                            | TokenType::Gt
2937                            | TokenType::Lte
2938                            | TokenType::Gte
2939                            | TokenType::And
2940                            | TokenType::Or
2941                            | TokenType::Comma
2942                            | TokenType::Dot
2943                            | TokenType::In
2944                            | TokenType::Is
2945                            | TokenType::Not
2946                            | TokenType::Like
2947                            | TokenType::Between
2948                            | TokenType::Semicolon
2949                            | TokenType::RParen
2950                            | TokenType::As
2951                            | TokenType::DPipe
2952                            | TokenType::Amp
2953                            | TokenType::Pipe
2954                            | TokenType::LBracket
2955                    )
2956                };
2957            if (self.config.allow_trailing_commas
2958                || matches!(
2959                    self.config.dialect,
2960                    Some(crate::dialects::DialectType::ClickHouse)
2961                ))
2962                && (!from_is_column && self.check_from_keyword()
2963                    || self.check(TokenType::Where)
2964                    || self.check(TokenType::GroupBy)
2965                    || self.check(TokenType::Having)
2966                    || self.check(TokenType::Order)
2967                    || self.check(TokenType::Limit)
2968                    || self.check(TokenType::Union)
2969                    || self.check(TokenType::Intersect)
2970                    || (self.check(TokenType::Except) && !self.check_next(TokenType::LParen) && !self.check_next(TokenType::Comma))
2971                    || self.check(TokenType::Semicolon)
2972                    || self.check(TokenType::RParen)
2973                    // SETTINGS/FORMAT only as boundaries when NOT followed by ( or [ (function/column ref)
2974                    || (self.check(TokenType::Settings) && !self.check_next(TokenType::LParen) && !self.check_next(TokenType::LBracket))
2975                    || (self.check(TokenType::Format) && !self.check_next(TokenType::LParen))
2976                    || self.is_at_end())
2977            {
2978                break;
2979            }
2980        }
2981
2982        Ok(expressions)
2983    }
2984
2985    /// Parse DuckDB FROM-first query syntax
2986    /// FROM tbl = SELECT * FROM tbl
2987    /// FROM tbl SELECT col1, col2 = SELECT col1, col2 FROM tbl
2988    fn parse_from_first_query(&mut self) -> Result<Expression> {
2989        self.expect(TokenType::From)?;
2990
2991        // Parse the FROM clause (table references)
2992        let from = self.parse_from()?;
2993
2994        // Check if there's an explicit SELECT clause after FROM
2995        let expressions = if self.check(TokenType::Select) {
2996            self.skip(); // consume SELECT
2997            self.parse_select_expressions()?
2998        } else {
2999            // No explicit SELECT means SELECT *
3000            vec![Expression::Star(crate::expressions::Star {
3001                table: None,
3002                except: None,
3003                replace: None,
3004                rename: None,
3005                trailing_comments: Vec::new(),
3006                span: None,
3007            })]
3008        };
3009
3010        // Parse PREWHERE clause (ClickHouse specific)
3011        let prewhere = if self.match_token(TokenType::Prewhere) {
3012            Some(self.parse_expression()?)
3013        } else {
3014            None
3015        };
3016
3017        // Parse WHERE clause
3018        let where_clause = if self.match_token(TokenType::Where) {
3019            Some(Where {
3020                this: self.parse_expression()?,
3021            })
3022        } else {
3023            None
3024        };
3025
3026        // Parse GROUP BY
3027        let group_by = if self.match_token(TokenType::Group) {
3028            self.expect(TokenType::By)?;
3029            let mut groups = Vec::new();
3030            loop {
3031                groups.push(self.parse_expression()?);
3032                if !self.match_token(TokenType::Comma) {
3033                    break;
3034                }
3035            }
3036            Some(GroupBy {
3037                expressions: groups,
3038                all: None,
3039                totals: false,
3040                comments: Vec::new(),
3041            })
3042        } else {
3043            None
3044        };
3045
3046        // Parse HAVING
3047        let having = if self.match_token(TokenType::Having) {
3048            Some(Having {
3049                this: self.parse_expression()?,
3050                comments: Vec::new(),
3051            })
3052        } else {
3053            None
3054        };
3055
3056        // Parse ORDER BY
3057        let order_by = if self.match_token(TokenType::Order) {
3058            self.expect(TokenType::By)?;
3059            Some(self.parse_order_by()?)
3060        } else {
3061            None
3062        };
3063
3064        // Parse LIMIT
3065        let limit = if self.match_token(TokenType::Limit) {
3066            let first_expr = self.parse_expression()?;
3067            Some(Limit {
3068                this: first_expr,
3069                percent: false,
3070                comments: Vec::new(),
3071            })
3072        } else {
3073            None
3074        };
3075
3076        // Parse OFFSET
3077        let offset = if self.match_token(TokenType::Offset) {
3078            let expr = self.parse_expression()?;
3079            let rows = if self.match_token(TokenType::Row) || self.match_token(TokenType::Rows) {
3080                Some(true)
3081            } else {
3082                None
3083            };
3084            Some(Offset { this: expr, rows })
3085        } else {
3086            None
3087        };
3088
3089        // Build SELECT expression
3090        let select = Select {
3091            expressions,
3092            from: Some(from),
3093            joins: Vec::new(),
3094            lateral_views: Vec::new(),
3095            prewhere,
3096            where_clause,
3097            group_by,
3098            having,
3099            qualify: None,
3100            order_by,
3101            distribute_by: None,
3102            cluster_by: None,
3103            sort_by: None,
3104            limit,
3105            offset,
3106            limit_by: None,
3107            fetch: None,
3108            distinct: false,
3109            distinct_on: None,
3110            top: None,
3111            with: None,
3112            sample: None,
3113            settings: None,
3114            format: None,
3115            windows: None,
3116            hint: None,
3117            connect: None,
3118            into: None,
3119            locks: Vec::new(),
3120            for_xml: Vec::new(),
3121            leading_comments: Vec::new(),
3122            post_select_comments: Vec::new(),
3123            kind: None,
3124            operation_modifiers: Vec::new(),
3125            qualify_after_window: false,
3126            option: None,
3127            exclude: None,
3128        };
3129
3130        // Check for set operations (UNION, INTERSECT, EXCEPT)
3131        let result = Expression::Select(Box::new(select));
3132        self.parse_set_operation(result)
3133    }
3134
3135    /// Parse FROM clause
3136    fn parse_from(&mut self) -> Result<From> {
3137        let mut expressions = Vec::new();
3138
3139        loop {
3140            let table = self.parse_table_expression()?;
3141            expressions.push(table);
3142
3143            if !self.match_token(TokenType::Comma) {
3144                break;
3145            }
3146
3147            // Handle trailing comma in FROM clause (Snowflake allows this)
3148            // If next token is a clause boundary keyword or end of input, break
3149            // Note: For Redshift, UNPIVOT after comma is a table expression (SUPER object traversal),
3150            // so we don't treat it as a boundary in that case
3151            let is_redshift = matches!(
3152                self.config.dialect,
3153                Some(crate::dialects::DialectType::Redshift)
3154            );
3155            let is_unpivot_boundary = !is_redshift && self.check(TokenType::Unpivot);
3156            if self.is_at_end()
3157                || is_unpivot_boundary
3158                || matches!(
3159                    self.peek().token_type,
3160                    TokenType::Where
3161                        | TokenType::GroupBy
3162                        | TokenType::Having
3163                        | TokenType::Order
3164                        | TokenType::Limit
3165                        | TokenType::Offset
3166                        | TokenType::Union
3167                        | TokenType::Intersect
3168                        | TokenType::Except
3169                        | TokenType::Semicolon
3170                        | TokenType::RParen
3171                        | TokenType::Window
3172                        | TokenType::Qualify
3173                        | TokenType::Distribute
3174                        | TokenType::Cluster
3175                        | TokenType::Pivot
3176                )
3177            {
3178                break;
3179            }
3180        }
3181
3182        Ok(From { expressions })
3183    }
3184
3185    /// Parse a table expression (table name, subquery, etc.)
3186    fn parse_table_expression(&mut self) -> Result<Expression> {
3187        // Handle PostgreSQL ONLY modifier: FROM ONLY t1
3188        // ONLY prevents scanning child tables in inheritance hierarchy
3189        let has_only = self.match_token(TokenType::Only);
3190
3191        // Handle PostgreSQL ROWS FROM syntax:
3192        // ROWS FROM (func1(args) AS alias1(col1 type1), func2(args) AS alias2(col2 type2)) [WITH ORDINALITY] [AS alias(cols)]
3193        if self.match_text_seq(&["ROWS", "FROM"]) {
3194            return self.parse_rows_from();
3195        }
3196
3197        // Redshift UNPIVOT in FROM clause for SUPER object traversal:
3198        // UNPIVOT expr [AS val_alias AT attr_alias]
3199        // Examples:
3200        //   UNPIVOT c.c_orders[0]
3201        //   UNPIVOT c.c_orders AS val AT attr
3202        if self.match_token(TokenType::Unpivot) {
3203            return self.parse_redshift_unpivot_table();
3204        }
3205
3206        let mut expr = if self.check(TokenType::Values) && self.check_next(TokenType::LParen) {
3207            // VALUES as table expression: FROM (VALUES ...)
3208            // In ClickHouse, bare `values` without ( is a table name
3209            self.parse_values()?
3210        } else if self.check(TokenType::Values)
3211            && matches!(
3212                self.config.dialect,
3213                Some(crate::dialects::DialectType::ClickHouse)
3214            )
3215        {
3216            // ClickHouse: `values` as a table name (not followed by LParen)
3217            let token = self.advance();
3218            let ident = Identifier::new(token.text);
3219            let trailing_comments = self.previous_trailing_comments().to_vec();
3220            Expression::boxed_table(TableRef {
3221                name: ident,
3222                schema: None,
3223                catalog: None,
3224                alias: None,
3225                alias_explicit_as: false,
3226                column_aliases: Vec::new(),
3227                trailing_comments,
3228                when: None,
3229                only: false,
3230                final_: false,
3231                table_sample: None,
3232                hints: Vec::new(),
3233                system_time: None,
3234                partitions: Vec::new(),
3235                identifier_func: None,
3236                changes: None,
3237                version: None,
3238                span: None,
3239            })
3240        } else if self.check(TokenType::DAt) {
3241            // Snowflake stage reference: @stage_name or @"stage_name" or @namespace.stage/path
3242            self.parse_stage_reference()?
3243        } else if self.check(TokenType::Var) && self.peek().text.starts_with('@') {
3244            // Snowflake stage reference tokenized as Var: @mystage/path
3245            // When @ is followed by alphanumeric, tokenizer creates a Var token instead of DAt
3246            self.parse_stage_reference_from_var()?
3247        } else if self.check(TokenType::String) && self.peek().text.starts_with('@') {
3248            // Snowflake stage reference in string: '@mystage' or '@external/location'
3249            self.parse_stage_reference_from_string()?
3250        } else if self.match_token(TokenType::Lateral) {
3251            if self.check(TokenType::LParen) {
3252                // LATERAL (SELECT ...) or LATERAL (table_expression) or LATERAL (FROM ...) for DuckDB
3253                self.expect(TokenType::LParen)?;
3254                if self.check(TokenType::Select)
3255                    || self.check(TokenType::With)
3256                    || self.check(TokenType::From)
3257                {
3258                    let query = self.parse_statement()?;
3259                    self.expect(TokenType::RParen)?;
3260                    Expression::Subquery(Box::new(Subquery {
3261                        this: query,
3262                        alias: None,
3263                        column_aliases: Vec::new(),
3264                        order_by: None,
3265                        limit: None,
3266                        offset: None,
3267                        lateral: true,
3268                        modifiers_inside: false,
3269                        trailing_comments: Vec::new(),
3270                        distribute_by: None,
3271                        sort_by: None,
3272                        cluster_by: None,
3273                        inferred_type: None,
3274                    }))
3275                } else {
3276                    // LATERAL (table_function()) - parenthesized non-subquery
3277                    let table_expr = self.parse_table_expression()?;
3278                    self.expect(TokenType::RParen)?;
3279                    Expression::Subquery(Box::new(Subquery {
3280                        this: table_expr,
3281                        alias: None,
3282                        column_aliases: Vec::new(),
3283                        order_by: None,
3284                        limit: None,
3285                        offset: None,
3286                        lateral: true,
3287                        modifiers_inside: false,
3288                        trailing_comments: Vec::new(),
3289                        distribute_by: None,
3290                        sort_by: None,
3291                        cluster_by: None,
3292                        inferred_type: None,
3293                    }))
3294                }
3295            } else {
3296                // LATERAL function_name(args) [WITH ORDINALITY] [AS alias(columns)]
3297                // Parse function name
3298                let first_ident = self.expect_identifier_or_keyword_with_quoted()?;
3299                let first_name = first_ident.name.clone();
3300
3301                // Parse function arguments
3302                self.expect(TokenType::LParen)?;
3303                let args = if self.check(TokenType::RParen) {
3304                    Vec::new()
3305                } else {
3306                    self.parse_function_arguments()?
3307                };
3308                self.expect(TokenType::RParen)?;
3309
3310                // Handle UNNEST specially to create UnnestFunc expression
3311                let mut func_expr = if first_name.eq_ignore_ascii_case("UNNEST") {
3312                    let mut args_iter = args.into_iter();
3313                    let this = args_iter
3314                        .next()
3315                        .ok_or_else(|| self.parse_error("Expected expression in UNNEST"))?;
3316                    let expressions: Vec<Expression> = args_iter.collect();
3317                    Expression::Unnest(Box::new(crate::expressions::UnnestFunc {
3318                        this,
3319                        expressions,
3320                        with_ordinality: false,
3321                        alias: None,
3322                        offset_alias: None,
3323                    }))
3324                } else {
3325                    Expression::Function(Box::new(Function {
3326                        name: first_name,
3327                        args,
3328                        distinct: false,
3329                        trailing_comments: Vec::new(),
3330                        use_bracket_syntax: false,
3331                        no_parens: false,
3332                        quoted: false,
3333                        span: None,
3334                        inferred_type: None,
3335                    }))
3336                };
3337
3338                // Check for WITH ORDINALITY (Presto) or WITH OFFSET (BigQuery)
3339                let mut with_offset_alias: Option<crate::expressions::Identifier> = None;
3340                let ordinality = if self.match_token(TokenType::With) {
3341                    if self.match_token(TokenType::Ordinality) {
3342                        Some(Box::new(Expression::Boolean(BooleanLiteral {
3343                            value: true,
3344                        })))
3345                    } else if self.check(TokenType::Offset) || self.check_identifier("OFFSET") {
3346                        // BigQuery: WITH OFFSET [AS alias]
3347                        self.skip(); // consume OFFSET
3348                                        // Check for optional offset alias: WITH OFFSET AS y or WITH OFFSET y
3349                        if matches!(
3350                            self.config.dialect,
3351                            Some(crate::dialects::DialectType::BigQuery)
3352                        ) {
3353                            let has_as = self.match_token(TokenType::As);
3354                            if has_as
3355                                || self.check(TokenType::Identifier)
3356                                || self.check(TokenType::Var)
3357                            {
3358                                let alias_name = self.advance().text;
3359                                with_offset_alias = Some(crate::expressions::Identifier {
3360                                    name: alias_name,
3361                                    quoted: false,
3362                                    trailing_comments: Vec::new(),
3363                                    span: None,
3364                                });
3365                            }
3366                        }
3367                        Some(Box::new(Expression::Boolean(BooleanLiteral {
3368                            value: true,
3369                        })))
3370                    } else {
3371                        // Not ORDINALITY or OFFSET, put back WITH
3372                        self.current -= 1;
3373                        None
3374                    }
3375                } else {
3376                    None
3377                };
3378
3379                // Update the inner UnnestFunc with WITH ORDINALITY/OFFSET info
3380                if ordinality.is_some() {
3381                    if let Expression::Unnest(ref mut u) = func_expr {
3382                        u.with_ordinality = true;
3383                        u.offset_alias = with_offset_alias;
3384                    }
3385                }
3386
3387                // Parse optional alias: AS alias or just alias
3388                let alias_ident = if self.match_token(TokenType::As) {
3389                    Some(self.expect_identifier_or_keyword_with_quoted()?)
3390                } else if !self.is_at_end()
3391                    && !self.check(TokenType::Comma)
3392                    && !self.check(TokenType::RParen)
3393                    && !self.check(TokenType::On)
3394                    && !self.check(TokenType::Cross)
3395                    && !self.check(TokenType::Inner)
3396                    && !self.check(TokenType::Left)
3397                    && !self.check(TokenType::Right)
3398                    && !self.check(TokenType::Full)
3399                    && !self.check(TokenType::Join)
3400                    && !self.check(TokenType::Where)
3401                    && !self.check(TokenType::Order)
3402                    && !self.check(TokenType::Limit)
3403                    && !self.check(TokenType::Semicolon)
3404                    && (self.check(TokenType::Identifier) || self.check(TokenType::Var))
3405                {
3406                    Some(self.expect_identifier_or_keyword_with_quoted()?)
3407                } else {
3408                    None
3409                };
3410                let alias_quoted = alias_ident.as_ref().map_or(false, |id| id.quoted);
3411                let alias = alias_ident.map(|id| id.name);
3412
3413                // Parse column aliases: (col1, col2, ...)
3414                let column_aliases = if alias.is_some() && self.match_token(TokenType::LParen) {
3415                    let mut cols = Vec::new();
3416                    loop {
3417                        cols.push(self.expect_identifier_or_keyword()?);
3418                        if !self.match_token(TokenType::Comma) {
3419                            break;
3420                        }
3421                    }
3422                    self.expect(TokenType::RParen)?;
3423                    cols
3424                } else {
3425                    Vec::new()
3426                };
3427
3428                Expression::Lateral(Box::new(Lateral {
3429                    this: Box::new(func_expr),
3430                    view: None,
3431                    outer: None,
3432                    alias,
3433                    alias_quoted,
3434                    cross_apply: None,
3435                    ordinality,
3436                    column_aliases,
3437                }))
3438            }
3439        } else if self.match_token(TokenType::LParen) {
3440            // Subquery or parenthesized set operation or (VALUES ...)
3441            if self.check(TokenType::Values) {
3442                // (VALUES (...), (...)) AS t(c1, c2) or (VALUES (0) foo(bar))
3443                let mut values = self.parse_values()?;
3444                self.expect(TokenType::RParen)?;
3445                // Extract alias from Values if present and move to Subquery
3446                let (alias, column_aliases) = if let Expression::Values(ref mut v) = values {
3447                    (v.alias.take(), std::mem::take(&mut v.column_aliases))
3448                } else {
3449                    (None, Vec::new())
3450                };
3451                Expression::Subquery(Box::new(Subquery {
3452                    this: values,
3453                    alias,
3454                    column_aliases,
3455                    order_by: None,
3456                    limit: None,
3457                    offset: None,
3458                    distribute_by: None,
3459                    sort_by: None,
3460                    cluster_by: None,
3461                    lateral: false,
3462                    modifiers_inside: false,
3463                    trailing_comments: self.previous_trailing_comments().to_vec(),
3464                    inferred_type: None,
3465                }))
3466            } else if self.check(TokenType::Select)
3467                || self.check(TokenType::With)
3468                || self.check(TokenType::Pivot)
3469                || self.check(TokenType::Unpivot)
3470                || self.check(TokenType::From)
3471                || self.check(TokenType::Merge)
3472                || self.check(TokenType::Describe)
3473                || (self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("EXPLAIN"))
3474                || (self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("SUMMARIZE"))
3475            {
3476                let query = self.parse_statement()?;
3477                self.expect(TokenType::RParen)?;
3478                let trailing = self.previous_trailing_comments().to_vec();
3479                // Check for set operations after parenthesized query
3480                // If there's a set operation, wrap query in Subquery first to preserve parens
3481                // e.g., (SELECT 1) UNION (SELECT 2) - the left operand needs Subquery wrapper
3482                let result = if self.check(TokenType::Union)
3483                    || self.check(TokenType::Intersect)
3484                    || self.check(TokenType::Except)
3485                {
3486                    let left = Expression::Subquery(Box::new(Subquery {
3487                        this: query,
3488                        alias: None,
3489                        column_aliases: Vec::new(),
3490                        order_by: None,
3491                        limit: None,
3492                        offset: None,
3493                        lateral: false,
3494                        modifiers_inside: false,
3495                        trailing_comments: Vec::new(),
3496                        distribute_by: None,
3497                        sort_by: None,
3498                        cluster_by: None,
3499                        inferred_type: None,
3500                    }));
3501                    self.parse_set_operation(left)?
3502                } else {
3503                    query
3504                };
3505                Expression::Subquery(Box::new(Subquery {
3506                    this: result,
3507                    alias: None,
3508                    column_aliases: Vec::new(),
3509                    order_by: None,
3510                    limit: None,
3511                    offset: None,
3512                    distribute_by: None,
3513                    sort_by: None,
3514                    cluster_by: None,
3515                    lateral: false,
3516                    modifiers_inside: false,
3517                    trailing_comments: trailing,
3518                    inferred_type: None,
3519                }))
3520            } else if self.check(TokenType::LParen) {
3521                // Nested parens like ((SELECT ...)) or ((x))
3522                // Also handles ((SELECT 1) UNION (SELECT 2)) - set operations inside parens
3523                let inner = self.parse_table_expression()?;
3524
3525                // Handle alias on subquery before set operation: ((SELECT 1) AS a UNION ALL (SELECT 2) AS b)
3526                let inner = if self.match_token(TokenType::As) {
3527                    let alias = self.expect_identifier()?;
3528                    if let Expression::Subquery(mut subq) = inner {
3529                        subq.alias = Some(Identifier::new(alias));
3530                        Expression::Subquery(subq)
3531                    } else {
3532                        Expression::Alias(Box::new(Alias::new(inner, Identifier::new(alias))))
3533                    }
3534                } else if self.is_identifier_token()
3535                    && !self.check(TokenType::Union)
3536                    && !self.check(TokenType::Intersect)
3537                    && !self.check(TokenType::Except)
3538                    && !self.check(TokenType::Cross)
3539                    && !self.check(TokenType::Inner)
3540                    && !self.check(TokenType::Left)
3541                    && !self.check(TokenType::Right)
3542                    && !self.check(TokenType::Full)
3543                    && !self.check(TokenType::Join)
3544                    && !self.check(TokenType::Order)
3545                    && !self.check(TokenType::Limit)
3546                    && !self.check(TokenType::Offset)
3547                    && !self.check(TokenType::Xor)
3548                {
3549                    // Implicit alias (no AS keyword)
3550                    let alias = self.expect_identifier()?;
3551                    if let Expression::Subquery(mut subq) = inner {
3552                        subq.alias = Some(Identifier::new(alias));
3553                        Expression::Subquery(subq)
3554                    } else {
3555                        Expression::Alias(Box::new(Alias::new(inner, Identifier::new(alias))))
3556                    }
3557                } else {
3558                    inner
3559                };
3560
3561                // ClickHouse: ((SELECT 1) AS x, (SELECT 2) AS y) — tuple of aliased subqueries
3562                if matches!(
3563                    self.config.dialect,
3564                    Some(crate::dialects::DialectType::ClickHouse)
3565                ) && self.check(TokenType::Comma)
3566                {
3567                    let mut exprs = vec![inner];
3568                    while self.match_token(TokenType::Comma) {
3569                        if self.check(TokenType::RParen) {
3570                            break;
3571                        }
3572                        let e = self.parse_expression()?;
3573                        exprs.push(e);
3574                    }
3575                    self.expect(TokenType::RParen)?;
3576                    return Ok(Expression::Tuple(Box::new(Tuple { expressions: exprs })));
3577                }
3578
3579                // Check for set operations after the first table expression
3580                let had_set_operation = self.check(TokenType::Union)
3581                    || self.check(TokenType::Intersect)
3582                    || self.check(TokenType::Except);
3583                let result = if had_set_operation {
3584                    // This is a set operation like ((SELECT 1) UNION (SELECT 2))
3585                    // Wrap inner in a subquery-like expression and parse set operation
3586                    let set_result = self.parse_set_operation(inner)?;
3587                    set_result
3588                } else if self.check(TokenType::Cross)
3589                    || self.check(TokenType::Inner)
3590                    || self.check(TokenType::Left)
3591                    || self.check(TokenType::Right)
3592                    || self.check(TokenType::Full)
3593                    || self.check(TokenType::Join)
3594                {
3595                    // This is a join: ((SELECT 1) CROSS JOIN (SELECT 2))
3596                    let joins = self.parse_joins()?;
3597                    let lateral_views = self.parse_lateral_views()?;
3598                    Expression::JoinedTable(Box::new(JoinedTable {
3599                        left: inner,
3600                        joins,
3601                        lateral_views,
3602                        alias: None,
3603                    }))
3604                } else {
3605                    inner
3606                };
3607
3608                // Handle ORDER BY, LIMIT, OFFSET after set operations inside parens
3609                let result = if self.check(TokenType::Order) {
3610                    // Wrap in a subquery with order/limit
3611                    self.expect(TokenType::Order)?;
3612                    self.expect(TokenType::By)?;
3613                    let order_by = self.parse_order_by()?;
3614                    let limit = if self.match_token(TokenType::Limit) {
3615                        Some(Limit {
3616                            this: self.parse_expression()?,
3617                            percent: false,
3618                            comments: Vec::new(),
3619                        })
3620                    } else {
3621                        None
3622                    };
3623                    let offset = if self.match_token(TokenType::Offset) {
3624                        Some(Offset {
3625                            this: self.parse_expression()?,
3626                            rows: None,
3627                        })
3628                    } else {
3629                        None
3630                    };
3631                    Expression::Subquery(Box::new(Subquery {
3632                        this: result,
3633                        alias: None,
3634                        column_aliases: Vec::new(),
3635                        order_by: Some(order_by),
3636                        limit,
3637                        offset,
3638                        distribute_by: None,
3639                        sort_by: None,
3640                        cluster_by: None,
3641                        lateral: false,
3642                        modifiers_inside: true, // ORDER BY was inside the parens
3643                        trailing_comments: Vec::new(),
3644                        inferred_type: None,
3645                    }))
3646                } else if self.check(TokenType::Limit) || self.check(TokenType::Offset) {
3647                    // LIMIT/OFFSET without ORDER BY
3648                    let limit = if self.match_token(TokenType::Limit) {
3649                        Some(Limit {
3650                            this: self.parse_expression()?,
3651                            percent: false,
3652                            comments: Vec::new(),
3653                        })
3654                    } else {
3655                        None
3656                    };
3657                    let offset = if self.match_token(TokenType::Offset) {
3658                        Some(Offset {
3659                            this: self.parse_expression()?,
3660                            rows: None,
3661                        })
3662                    } else {
3663                        None
3664                    };
3665                    Expression::Subquery(Box::new(Subquery {
3666                        this: result,
3667                        alias: None,
3668                        column_aliases: Vec::new(),
3669                        order_by: None,
3670                        limit,
3671                        offset,
3672                        distribute_by: None,
3673                        sort_by: None,
3674                        cluster_by: None,
3675                        lateral: false,
3676                        modifiers_inside: true, // LIMIT/OFFSET was inside the parens
3677                        trailing_comments: Vec::new(),
3678                        inferred_type: None,
3679                    }))
3680                } else {
3681                    result
3682                };
3683
3684                self.expect(TokenType::RParen)?;
3685                // Wrap result in Paren to preserve the outer parentheses when needed
3686                // Cases:
3687                // - ((SELECT 1)) -> Paren(Subquery(Select)) - inner was subquery of SELECT, wrap in Paren
3688                // - ((SELECT 1) UNION (SELECT 2)) -> Subquery(Union) - recursive call handled set op, don't add Paren
3689                // - ((SELECT 1) AS a UNION ALL ...) -> Union - we handled set op, need to add Paren
3690                // - (((SELECT 1) UNION SELECT 2) ORDER BY x) -> Subquery with modifiers_inside=true
3691                let had_modifiers = matches!(&result, Expression::Subquery(s) if s.order_by.is_some() || s.limit.is_some() || s.offset.is_some());
3692                let result_is_subquery_of_set_op = matches!(&result, Expression::Subquery(s) if matches!(&s.this, Expression::Union(_) | Expression::Intersect(_) | Expression::Except(_)));
3693                if had_modifiers || result_is_subquery_of_set_op {
3694                    // Subquery with modifiers or Subquery(Union) - already has proper structure
3695                    result
3696                } else {
3697                    // All other cases need Paren wrapper to preserve outer parentheses
3698                    Expression::Paren(Box::new(Paren {
3699                        this: result,
3700                        trailing_comments: Vec::new(),
3701                    }))
3702                }
3703            } else if self.is_identifier_token()
3704                || self.is_safe_keyword_as_identifier()
3705                || self.can_be_alias_keyword()
3706            {
3707                // Parenthesized join expression: (tbl1 CROSS JOIN tbl2) or just (x)
3708                // Also allow safe keywords and alias keywords (all, left, etc.) as table names
3709                let (left, joins) = self.parse_table_expression_with_joins()?;
3710                // Parse LATERAL VIEW after joins: (x CROSS JOIN foo LATERAL VIEW EXPLODE(y))
3711                let lateral_views = self.parse_lateral_views()?;
3712                self.expect(TokenType::RParen)?;
3713                if joins.is_empty() && lateral_views.is_empty() {
3714                    // Just a parenthesized table expression, wrap in Paren to preserve parens
3715                    Expression::Paren(Box::new(Paren {
3716                        this: left,
3717                        trailing_comments: Vec::new(),
3718                    }))
3719                } else {
3720                    // Create a JoinedTable
3721                    Expression::JoinedTable(Box::new(JoinedTable {
3722                        left,
3723                        joins,
3724                        lateral_views,
3725                        alias: None, // Alias is parsed separately after this
3726                    }))
3727                }
3728            } else {
3729                let query = self.parse_statement()?;
3730                self.expect(TokenType::RParen)?;
3731                Expression::Subquery(Box::new(Subquery {
3732                    this: query,
3733                    alias: None,
3734                    column_aliases: Vec::new(),
3735                    order_by: None,
3736                    limit: None,
3737                    offset: None,
3738                    distribute_by: None,
3739                    sort_by: None,
3740                    cluster_by: None,
3741                    lateral: false,
3742                    modifiers_inside: false,
3743                    trailing_comments: self.previous_trailing_comments().to_vec(),
3744                    inferred_type: None,
3745                }))
3746            }
3747        } else if self.is_identifier_token() || self.is_safe_keyword_as_identifier() || self.can_be_alias_keyword()
3748            || (matches!(self.config.dialect, Some(crate::dialects::DialectType::BigQuery)) && self.check(TokenType::Number))
3749            || self.is_mysql_numeric_identifier()
3750            // PIVOT/UNPIVOT can be table names when not followed by (
3751            || (self.check(TokenType::Pivot) && !self.check_next(TokenType::LParen))
3752            || (self.check(TokenType::Unpivot) && !self.check_next(TokenType::LParen))
3753            // ClickHouse: braced query parameters as table names {db:Identifier}.table
3754            || (matches!(self.config.dialect, Some(crate::dialects::DialectType::ClickHouse)) && self.check(TokenType::LBrace))
3755            // ClickHouse: allow union/except/intersect as table names when not followed by ALL/DISTINCT/SELECT/(
3756            || (matches!(self.config.dialect, Some(crate::dialects::DialectType::ClickHouse))
3757                && (self.check(TokenType::Union) || self.check(TokenType::Except) || self.check(TokenType::Intersect))
3758                && !self.check_next(TokenType::All) && !self.check_next(TokenType::Distinct)
3759                && !self.check_next(TokenType::Select) && !self.check_next(TokenType::LParen))
3760        {
3761            // Table name - could be simple, qualified, or table function
3762            // Also allow safe keywords (like 'table', 'view', 'case', 'all', etc.) as table names
3763            // BigQuery: also allows numeric table parts and hyphenated identifiers
3764            // MySQL: allows numeric-starting identifiers (e.g., 00f, 1d)
3765
3766            // DuckDB prefix alias syntax: alias: table (e.g., "foo: bar" means "bar AS foo")
3767            // Check if next token is COLON (but not :: which is DCOLON for casts)
3768            if matches!(
3769                self.config.dialect,
3770                Some(crate::dialects::DialectType::DuckDB)
3771            ) && self.check_next(TokenType::Colon)
3772                && !(self.current + 2 < self.tokens.len()
3773                    && self.tokens[self.current + 2].token_type == TokenType::Colon)
3774            {
3775                // Parse the alias identifier
3776                let alias_ident = self.parse_bigquery_table_part()?;
3777                let pre_alias_comments = self.previous_trailing_comments().to_vec();
3778                // Consume the colon
3779                self.expect(TokenType::Colon)?;
3780                let colon_comments = self.previous_trailing_comments().to_vec();
3781                // Parse the actual table expression recursively
3782                let mut table_expr = self.parse_table_expression()?;
3783                // Merge comments
3784                let mut all_comments = pre_alias_comments;
3785                all_comments.extend(colon_comments);
3786                // Apply the alias to the table expression
3787                match &mut table_expr {
3788                    Expression::Table(ref mut t) => {
3789                        t.alias = Some(alias_ident);
3790                        t.alias_explicit_as = true; // Output AS keyword (required by expected format)
3791                                                    // Store prefix alias comments - they should come BEFORE the table's trailing comments
3792                                                    // For "foo /* bla */: bar /* baz */", output is "bar AS foo /* bla */ /* baz */"
3793                                                    // So alias comments (/* bla */) come first, then table comments (/* baz */)
3794                        if !all_comments.is_empty() {
3795                            let existing_comments = std::mem::take(&mut t.trailing_comments);
3796                            t.trailing_comments = all_comments;
3797                            t.trailing_comments.extend(existing_comments);
3798                        }
3799                    }
3800                    Expression::Subquery(ref mut s) => {
3801                        s.alias = Some(alias_ident);
3802                    }
3803                    Expression::Function(ref mut _f) => {
3804                        // Wrap function in alias
3805                        return Ok(Expression::Alias(Box::new(Alias {
3806                            this: table_expr,
3807                            alias: alias_ident,
3808                            column_aliases: Vec::new(),
3809                            pre_alias_comments: all_comments,
3810                            trailing_comments: Vec::new(),
3811                            inferred_type: None,
3812                        })));
3813                    }
3814                    _ => {
3815                        // For other expressions, wrap in Alias
3816                        return Ok(Expression::Alias(Box::new(Alias {
3817                            this: table_expr,
3818                            alias: alias_ident,
3819                            column_aliases: Vec::new(),
3820                            pre_alias_comments: all_comments,
3821                            trailing_comments: Vec::new(),
3822                            inferred_type: None,
3823                        })));
3824                    }
3825                }
3826                return Ok(table_expr);
3827            }
3828
3829            let first_ident = self.parse_bigquery_table_part()?;
3830            let first_name = first_ident.name.clone();
3831
3832            // Check for qualified name (schema.table) or table function
3833            if self.match_token(TokenType::Dot) {
3834                // Handle TSQL a..b syntax (database..table with empty schema)
3835                if self.check(TokenType::Dot) {
3836                    // Two consecutive dots: a..b means catalog..table (empty schema)
3837                    self.skip(); // consume second dot
3838                    let table_ident = self.parse_bigquery_table_part()?;
3839                    let trailing_comments = self.previous_trailing_comments().to_vec();
3840                    return Ok(Expression::boxed_table(TableRef {
3841                        catalog: Some(first_ident),
3842                        schema: Some(Identifier::new("")), // Empty schema represents ..
3843                        name: table_ident,
3844                        alias: None,
3845                        alias_explicit_as: false,
3846                        column_aliases: Vec::new(),
3847                        trailing_comments,
3848                        when: None,
3849                        only: false,
3850                        final_: false,
3851                        table_sample: None,
3852                        hints: Vec::new(),
3853                        system_time: None,
3854                        partitions: Vec::new(),
3855                        identifier_func: None,
3856                        changes: None,
3857                        version: None,
3858                        span: None,
3859                    }));
3860                }
3861
3862                // BigQuery: handle x.* wildcard table reference (e.g., SELECT * FROM x.*)
3863                // After the first dot, if we see a Star token, it's a wildcard table name
3864                if matches!(
3865                    self.config.dialect,
3866                    Some(crate::dialects::DialectType::BigQuery)
3867                ) && self.check(TokenType::Star)
3868                {
3869                    self.skip(); // consume *
3870                    let trailing_comments = self.previous_trailing_comments().to_vec();
3871                    return Ok(Expression::boxed_table(TableRef {
3872                        catalog: None,
3873                        schema: Some(first_ident),
3874                        name: Identifier::new("*"),
3875                        alias: None,
3876                        alias_explicit_as: false,
3877                        column_aliases: Vec::new(),
3878                        trailing_comments,
3879                        when: None,
3880                        only: false,
3881                        final_: false,
3882                        table_sample: None,
3883                        hints: Vec::new(),
3884                        system_time: None,
3885                        partitions: Vec::new(),
3886                        identifier_func: None,
3887                        changes: None,
3888                        version: None,
3889                        span: None,
3890                    }));
3891                }
3892
3893                // schema.table or schema.function()
3894                // Allow keywords as table/schema names (e.g., schema.table, catalog.view)
3895                let second_ident = self.parse_bigquery_table_part()?;
3896                let second_name = second_ident.name.clone();
3897
3898                if self.match_token(TokenType::Dot) {
3899                    // BigQuery: handle a.b.* wildcard table reference
3900                    if matches!(
3901                        self.config.dialect,
3902                        Some(crate::dialects::DialectType::BigQuery)
3903                    ) && self.check(TokenType::Star)
3904                    {
3905                        self.skip(); // consume *
3906                        let trailing_comments = self.previous_trailing_comments().to_vec();
3907                        return Ok(Expression::boxed_table(TableRef {
3908                            catalog: Some(first_ident),
3909                            schema: Some(second_ident),
3910                            name: Identifier::new("*"),
3911                            alias: None,
3912                            alias_explicit_as: false,
3913                            column_aliases: Vec::new(),
3914                            trailing_comments,
3915                            when: None,
3916                            only: false,
3917                            final_: false,
3918                            table_sample: None,
3919                            hints: Vec::new(),
3920                            system_time: None,
3921                            partitions: Vec::new(),
3922                            identifier_func: None,
3923                            changes: None,
3924                            version: None,
3925                            span: None,
3926                        }));
3927                    }
3928                    // catalog.schema.table or catalog.schema.function()
3929                    let third_ident = self.parse_bigquery_table_part()?;
3930                    let third_name = third_ident.name.clone();
3931
3932                    // Check for 4-part name (e.g., project.dataset.INFORMATION_SCHEMA.TABLES)
3933                    if self.match_token(TokenType::Dot) {
3934                        let fourth_ident = self.parse_bigquery_table_part()?;
3935                        // BigQuery wildcard table suffix: a.b.c.d* matches all tables starting with d
3936                        let mut table_name = fourth_ident;
3937                        if matches!(
3938                            self.config.dialect,
3939                            Some(crate::dialects::DialectType::BigQuery)
3940                        ) && self.check(TokenType::Star)
3941                            && self.is_connected()
3942                        {
3943                            self.skip(); // consume *
3944                            table_name.name.push('*');
3945                        }
3946                        let trailing_comments = self.previous_trailing_comments().to_vec();
3947                        // For 4-part names, combine first two parts as catalog, third as schema
3948                        Expression::boxed_table(TableRef {
3949                            catalog: Some(Identifier::new(format!(
3950                                "{}.{}",
3951                                first_name, second_name
3952                            ))),
3953                            schema: Some(third_ident),
3954                            name: table_name,
3955                            alias: None,
3956                            alias_explicit_as: false,
3957                            column_aliases: Vec::new(),
3958                            trailing_comments,
3959                            when: None,
3960                            only: false,
3961                            final_: false,
3962                            table_sample: None,
3963                            hints: Vec::new(),
3964                            system_time: None,
3965                            partitions: Vec::new(),
3966                            identifier_func: None,
3967                            changes: None,
3968                            version: None,
3969                            span: None,
3970                        })
3971                    } else if self.match_token(TokenType::LParen) {
3972                        // catalog.schema.function() - table-valued function
3973                        let args = if self.check(TokenType::RParen) {
3974                            Vec::new()
3975                        } else {
3976                            self.parse_function_arguments()?
3977                        };
3978                        self.expect(TokenType::RParen)?;
3979                        let trailing_comments = self.previous_trailing_comments().to_vec();
3980                        Expression::Function(Box::new(Function {
3981                            name: format!("{}.{}.{}", first_name, second_name, third_name),
3982                            args,
3983                            distinct: false,
3984                            trailing_comments,
3985                            use_bracket_syntax: false,
3986                            no_parens: false,
3987                            quoted: false,
3988                            span: None,
3989                            inferred_type: None,
3990                        }))
3991                    } else {
3992                        // catalog.schema.table
3993                        // BigQuery wildcard table suffix: x.y.z* matches all tables starting with z
3994                        let mut table_name = third_ident;
3995                        if matches!(
3996                            self.config.dialect,
3997                            Some(crate::dialects::DialectType::BigQuery)
3998                        ) && self.check(TokenType::Star)
3999                            && self.is_connected()
4000                        {
4001                            self.skip(); // consume *
4002                            table_name.name.push('*');
4003                        }
4004                        let trailing_comments = self.previous_trailing_comments().to_vec();
4005                        Expression::boxed_table(TableRef {
4006                            catalog: Some(first_ident),
4007                            schema: Some(second_ident),
4008                            name: table_name,
4009                            alias: None,
4010                            alias_explicit_as: false,
4011                            column_aliases: Vec::new(),
4012                            trailing_comments,
4013                            when: None,
4014                            only: false,
4015                            final_: false,
4016                            table_sample: None,
4017                            hints: Vec::new(),
4018                            system_time: None,
4019                            partitions: Vec::new(),
4020                            identifier_func: None,
4021                            changes: None,
4022                            version: None,
4023                            span: None,
4024                        })
4025                    }
4026                } else if self.match_token(TokenType::LParen) {
4027                    // schema.function() - table-valued function
4028                    let args = if self.check(TokenType::RParen) {
4029                        Vec::new()
4030                    } else {
4031                        self.parse_function_arguments()?
4032                    };
4033                    self.expect(TokenType::RParen)?;
4034                    let trailing_comments = self.previous_trailing_comments().to_vec();
4035                    Expression::Function(Box::new(Function {
4036                        name: format!("{}.{}", first_name, second_name),
4037                        args,
4038                        distinct: false,
4039                        trailing_comments,
4040                        use_bracket_syntax: false,
4041                        no_parens: false,
4042                        quoted: false,
4043                        span: None,
4044                        inferred_type: None,
4045                    }))
4046                } else {
4047                    // schema.table
4048                    // BigQuery wildcard table suffix: x.y* matches all tables starting with y
4049                    let mut table_name = second_ident;
4050                    if matches!(
4051                        self.config.dialect,
4052                        Some(crate::dialects::DialectType::BigQuery)
4053                    ) && self.check(TokenType::Star)
4054                        && self.is_connected()
4055                    {
4056                        self.skip(); // consume *
4057                        table_name.name.push('*');
4058                    }
4059                    let trailing_comments = self.previous_trailing_comments().to_vec();
4060                    Expression::boxed_table(TableRef {
4061                        catalog: None,
4062                        schema: Some(first_ident),
4063                        name: table_name,
4064                        alias: None,
4065                        alias_explicit_as: false,
4066                        column_aliases: Vec::new(),
4067                        trailing_comments,
4068                        when: None,
4069                        only: false,
4070                        final_: false,
4071                        table_sample: None,
4072                        hints: Vec::new(),
4073                        system_time: None,
4074                        partitions: Vec::new(),
4075                        identifier_func: None,
4076                        changes: None,
4077                        version: None,
4078                        span: None,
4079                    })
4080                }
4081            } else if self.match_token(TokenType::LParen) {
4082                // Handle JSON_TABLE specially - it has COLUMNS clause syntax
4083                if first_name.eq_ignore_ascii_case("JSON_TABLE") {
4084                    // Parse the JSON expression (use parse_bitwise to avoid consuming FORMAT)
4085                    let this = self
4086                        .parse_bitwise()?
4087                        .unwrap_or(Expression::Null(crate::expressions::Null));
4088
4089                    // Check for FORMAT JSON after the expression
4090                    let this_with_format = if self.match_text_seq(&["FORMAT", "JSON"]) {
4091                        Expression::JSONFormat(Box::new(crate::expressions::JSONFormat {
4092                            this: Some(Box::new(this)),
4093                            options: Vec::new(),
4094                            is_json: None,
4095                            to_json: None,
4096                        }))
4097                    } else {
4098                        this
4099                    };
4100
4101                    // Parse path (after comma)
4102                    let path = if self.match_token(TokenType::Comma) {
4103                        if let Some(s) = self.parse_string()? {
4104                            Some(Box::new(s))
4105                        } else {
4106                            None
4107                        }
4108                    } else {
4109                        None
4110                    };
4111
4112                    // Oracle uses "ERROR ON ERROR" (value then behavior) instead of "ON ERROR ERROR"
4113                    // Parse error handling: ERROR ON ERROR or NULL ON ERROR
4114                    let error_handling = if self.match_identifier("ERROR")
4115                        && self.match_text_seq(&["ON", "ERROR"])
4116                    {
4117                        Some(Box::new(Expression::Var(Box::new(Var {
4118                            this: "ERROR ON ERROR".to_string(),
4119                        }))))
4120                    } else if self.match_text_seq(&["NULL", "ON", "ERROR"]) {
4121                        Some(Box::new(Expression::Var(Box::new(Var {
4122                            this: "NULL ON ERROR".to_string(),
4123                        }))))
4124                    } else {
4125                        None
4126                    };
4127
4128                    // Parse empty handling: ERROR ON EMPTY or NULL ON EMPTY
4129                    let empty_handling = if self.match_identifier("ERROR")
4130                        && self.match_text_seq(&["ON", "EMPTY"])
4131                    {
4132                        Some(Box::new(Expression::Var(Box::new(Var {
4133                            this: "ERROR ON EMPTY".to_string(),
4134                        }))))
4135                    } else if self.match_text_seq(&["NULL", "ON", "EMPTY"]) {
4136                        Some(Box::new(Expression::Var(Box::new(Var {
4137                            this: "NULL ON EMPTY".to_string(),
4138                        }))))
4139                    } else {
4140                        None
4141                    };
4142
4143                    // Parse COLUMNS clause
4144                    let schema = self.parse_json_table_columns()?;
4145
4146                    self.expect(TokenType::RParen)?;
4147
4148                    Expression::JSONTable(Box::new(JSONTable {
4149                        this: Box::new(this_with_format),
4150                        schema: schema.map(Box::new),
4151                        path,
4152                        error_handling,
4153                        empty_handling,
4154                    }))
4155                } else if first_name.eq_ignore_ascii_case("XMLTABLE") {
4156                    // Handle XMLTABLE specially - it has COLUMNS clause syntax
4157                    // XMLTABLE([XMLNAMESPACES(...),] '/xpath' PASSING xml_doc COLUMNS ...)
4158                    if let Some(xml_table) = self.parse_xml_table()? {
4159                        self.expect(TokenType::RParen)?;
4160                        xml_table
4161                    } else {
4162                        return Err(self.parse_error("Failed to parse XMLTABLE"));
4163                    }
4164                } else if first_name.eq_ignore_ascii_case("OPENJSON") {
4165                    // Handle OPENJSON specially - it has WITH clause for column definitions
4166                    // OPENJSON(json[, path]) [WITH (col1 type1 'path' [AS JSON], ...)]
4167                    if let Some(openjson_expr) = self.parse_open_json()? {
4168                        openjson_expr
4169                    } else {
4170                        return Err(self.parse_error("Failed to parse OPENJSON"));
4171                    }
4172                } else if first_name.eq_ignore_ascii_case("SEMANTIC_VIEW") {
4173                    // Handle SEMANTIC_VIEW specially - it has METRICS/DIMENSIONS/FACTS/WHERE syntax
4174                    // SEMANTIC_VIEW(table METRICS a.b, a.c DIMENSIONS a.b, a.c WHERE expr)
4175                    let semantic_view = self.parse_semantic_view()?;
4176                    self.expect(TokenType::RParen)?;
4177                    semantic_view
4178                } else if (first_name.eq_ignore_ascii_case("view")
4179                    || first_name.eq_ignore_ascii_case("merge"))
4180                    && (self.check(TokenType::Select) || self.check(TokenType::With))
4181                {
4182                    // ClickHouse: view(SELECT ...) and merge(SELECT ...) table functions
4183                    // contain a subquery as the argument
4184                    let query = self.parse_statement()?;
4185                    self.expect(TokenType::RParen)?;
4186                    let trailing_comments = self.previous_trailing_comments().to_vec();
4187                    Expression::Function(Box::new(Function {
4188                        name: first_name.to_string(),
4189                        args: vec![query],
4190                        distinct: false,
4191                        trailing_comments,
4192                        use_bracket_syntax: false,
4193                        no_parens: false,
4194                        quoted: false,
4195                        span: None,
4196                        inferred_type: None,
4197                    }))
4198                } else {
4199                    // Simple table function like UNNEST(), GAP_FILL(), etc.
4200                    let args = if self.check(TokenType::RParen) {
4201                        Vec::new()
4202                    } else {
4203                        self.parse_function_arguments()?
4204                    };
4205                    self.expect(TokenType::RParen)?;
4206                    let trailing_comments = self.previous_trailing_comments().to_vec();
4207
4208                    // Handle UNNEST specially to create UnnestFunc expression
4209                    if first_name.eq_ignore_ascii_case("UNNEST") {
4210                        // Check for WITH ORDINALITY (Presto) or WITH OFFSET (BigQuery)
4211                        // Both are semantically the same - provide an ordinal/offset column
4212                        let with_ordinality = self
4213                            .match_keywords(&[TokenType::With, TokenType::Ordinality])
4214                            || self.match_text_seq(&["WITH", "OFFSET"]);
4215                        // If WITH OFFSET matched, check for optional offset alias: WITH OFFSET AS y or WITH OFFSET y
4216                        let offset_alias = if with_ordinality
4217                            && matches!(
4218                                self.config.dialect,
4219                                Some(crate::dialects::DialectType::BigQuery)
4220                            ) {
4221                            let has_as = self.match_token(TokenType::As);
4222                            if has_as
4223                                || (self.check(TokenType::Identifier) || self.check(TokenType::Var))
4224                            {
4225                                let alias_name = self.advance().text;
4226                                Some(crate::expressions::Identifier {
4227                                    name: alias_name,
4228                                    quoted: false,
4229                                    trailing_comments: Vec::new(),
4230                                    span: None,
4231                                })
4232                            } else {
4233                                None
4234                            }
4235                        } else {
4236                            None
4237                        };
4238                        let mut args_iter = args.into_iter();
4239                        let this = args_iter
4240                            .next()
4241                            .ok_or_else(|| self.parse_error("Expected expression in UNNEST"))?;
4242                        let expressions: Vec<Expression> = args_iter.collect();
4243                        Expression::Unnest(Box::new(crate::expressions::UnnestFunc {
4244                            this,
4245                            expressions,
4246                            with_ordinality,
4247                            alias: None,
4248                            offset_alias,
4249                        }))
4250                    } else {
4251                        // Check for WITH ORDINALITY after any table-valued function
4252                        let with_ordinality =
4253                            self.match_keywords(&[TokenType::With, TokenType::Ordinality]);
4254                        let func_name = if with_ordinality {
4255                            format!("{} WITH ORDINALITY", first_name)
4256                        } else {
4257                            first_name.clone()
4258                        };
4259                        let func = Function {
4260                            name: func_name,
4261                            args,
4262                            distinct: false,
4263                            trailing_comments,
4264                            use_bracket_syntax: false,
4265                            no_parens: false,
4266                            quoted: false,
4267                            span: None,
4268                            inferred_type: None,
4269                        };
4270                        Expression::Function(Box::new(func))
4271                    }
4272                }
4273            } else {
4274                // Simple table name
4275                // BigQuery wildcard table suffix: x* matches all tables starting with x
4276                let mut table_name = first_ident;
4277                if matches!(
4278                    self.config.dialect,
4279                    Some(crate::dialects::DialectType::BigQuery)
4280                ) && self.check(TokenType::Star)
4281                    && self.is_connected()
4282                {
4283                    self.skip(); // consume *
4284                    table_name.name.push('*');
4285                }
4286                let trailing_comments = self.previous_trailing_comments().to_vec();
4287                Expression::boxed_table(TableRef {
4288                    catalog: None,
4289                    schema: None,
4290                    name: table_name,
4291                    alias: None,
4292                    alias_explicit_as: false,
4293                    column_aliases: Vec::new(),
4294                    trailing_comments,
4295                    when: None,
4296                    only: false,
4297                    final_: false,
4298                    table_sample: None,
4299                    hints: Vec::new(),
4300                    system_time: None,
4301                    partitions: Vec::new(),
4302                    identifier_func: None,
4303                    changes: None,
4304                    version: None,
4305                    span: None,
4306                })
4307            }
4308        } else if self.check(TokenType::LBrace) {
4309            // ClickHouse query parameter: {name: Type}
4310            if let Some(param) = self.parse_clickhouse_braced_parameter()? {
4311                param
4312            } else {
4313                // Spark/Databricks widget template variable: {name}
4314                self.skip(); // consume {
4315                if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
4316                    let name_token = self.advance();
4317                    self.expect(TokenType::RBrace)?;
4318                    Expression::Parameter(Box::new(Parameter {
4319                        name: Some(name_token.text.clone()),
4320                        index: None,
4321                        style: ParameterStyle::Brace,
4322                        quoted: false,
4323                        string_quoted: false,
4324                        expression: None,
4325                    }))
4326                } else {
4327                    return Err(self.parse_error("Expected identifier after {"));
4328                }
4329            }
4330        } else if self.check(TokenType::Dollar) && self.check_next(TokenType::LBrace) {
4331            // Template variable as table reference: ${variable_name} or ${kind:name}
4332            // This is used in Databricks/Hive for parameterized queries
4333            self.skip(); // consume $
4334            self.skip(); // consume {
4335            if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
4336                let name_token = self.advance();
4337                // Check for ${kind:name} syntax (e.g., ${hiveconf:some_var})
4338                let expression = if self.match_token(TokenType::Colon) {
4339                    if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
4340                        let expr_token = self.advance();
4341                        Some(expr_token.text.clone())
4342                    } else {
4343                        return Err(self.parse_error("Expected identifier after : in ${...}"));
4344                    }
4345                } else {
4346                    None
4347                };
4348                self.expect(TokenType::RBrace)?;
4349                Expression::Parameter(Box::new(Parameter {
4350                    name: Some(name_token.text.clone()),
4351                    index: None,
4352                    style: ParameterStyle::DollarBrace,
4353                    quoted: false,
4354                    string_quoted: false,
4355                    expression,
4356                }))
4357            } else {
4358                return Err(self.parse_error("Expected identifier after ${"));
4359            }
4360        } else if self.check(TokenType::String) {
4361            // DuckDB allows string literals as table names: SELECT * FROM 'x.y'
4362            // Convert to a quoted identifier
4363            let string_token = self.advance();
4364            let table_name = Identifier {
4365                name: string_token.text.clone(),
4366                quoted: true,
4367                trailing_comments: Vec::new(),
4368                span: None,
4369            };
4370            let trailing_comments = self.previous_trailing_comments().to_vec();
4371            Expression::boxed_table(TableRef {
4372                catalog: None,
4373                schema: None,
4374                name: table_name,
4375                alias: None,
4376                alias_explicit_as: false,
4377                column_aliases: Vec::new(),
4378                trailing_comments,
4379                when: None,
4380                only: false,
4381                final_: false,
4382                table_sample: None,
4383                hints: Vec::new(),
4384                system_time: None,
4385                partitions: Vec::new(),
4386                identifier_func: None,
4387                changes: None,
4388                version: None,
4389                span: None,
4390            })
4391        } else {
4392            return Err(self.parse_error(format!(
4393                "Expected table name or subquery, got {:?}",
4394                self.peek().token_type
4395            )));
4396        };
4397
4398        // Postgres supports a wildcard (table) suffix operator, which is a no-op in this context.
4399        // e.g., FROM t1* means "include inherited tables". Matches Python sqlglot behavior.
4400        self.match_token(TokenType::Star);
4401
4402        // Check for Snowflake CHANGES clause: CHANGES (INFORMATION => ...) AT|BEFORE (...) END (...)
4403        // Must be checked before time travel since CHANGES includes its own AT/BEFORE clauses
4404        if self.check_keyword_text("CHANGES") {
4405            if let Some(changes_expr) = self.parse_changes()? {
4406                if let Expression::Table(ref mut table) = expr {
4407                    if let Expression::Changes(changes_box) = changes_expr {
4408                        table.changes = Some(changes_box);
4409                    }
4410                }
4411            }
4412        }
4413
4414        // Check for Snowflake time travel: BEFORE (STATEMENT => ...) or AT (TIMESTAMP => ...)
4415        if self.check(TokenType::Before) || self.check_keyword_text("AT") {
4416            if let Some(historical_expr) = self.parse_historical_data()? {
4417                // Attach historical data to the table expression
4418                if let Expression::Table(ref mut table) = expr {
4419                    if let Expression::HistoricalData(hd) = historical_expr {
4420                        table.when = Some(hd);
4421                    }
4422                }
4423            }
4424        }
4425
4426        // Check for TSQL FOR SYSTEM_TIME temporal clause (not BigQuery - handled post-alias)
4427        // Syntax: FOR SYSTEM_TIME AS OF expr
4428        //         FOR SYSTEM_TIME FROM expr TO expr
4429        //         FOR SYSTEM_TIME BETWEEN expr AND expr
4430        //         FOR SYSTEM_TIME CONTAINED IN (expr, expr)
4431        //         FOR SYSTEM_TIME ALL
4432        if !matches!(self.config.dialect, Some(crate::dialects::DialectType::BigQuery))
4433            && self.check(TokenType::For)
4434            && self.current + 1 < self.tokens.len()
4435            && self.tokens[self.current + 1]
4436                .text
4437                .eq_ignore_ascii_case("SYSTEM_TIME")
4438        {
4439            self.skip(); // consume FOR
4440            self.skip(); // consume SYSTEM_TIME
4441            let system_time_str = if self.match_token(TokenType::As) {
4442                // AS OF expr
4443                if self.check_keyword_text("OF") {
4444                    self.skip(); // consume OF
4445                    let start = self.current;
4446                    // Collect expression tokens until we hit a clause boundary
4447                    while !self.is_at_end()
4448                        && !self.check(TokenType::Semicolon)
4449                        && !self.check(TokenType::Where)
4450                        && !self.check(TokenType::Join)
4451                        && !self.check(TokenType::Left)
4452                        && !self.check(TokenType::Right)
4453                        && !self.check(TokenType::Inner)
4454                        && !self.check(TokenType::Outer)
4455                        && !self.check(TokenType::Full)
4456                        && !self.check(TokenType::Cross)
4457                        && !self.check(TokenType::Order)
4458                        && !self.check(TokenType::Group)
4459                        && !self.check(TokenType::Having)
4460                        && !self.check(TokenType::Limit)
4461                        && !self.check(TokenType::Union)
4462                        && !self.check(TokenType::Except)
4463                        && !self.check(TokenType::Intersect)
4464                        && !self.check(TokenType::As)
4465                        && !self.check(TokenType::Comma)
4466                        && !self.check(TokenType::RParen)
4467                        && !self.check(TokenType::With)
4468                        && !self.check(TokenType::Pivot)
4469                        && !self.check(TokenType::Unpivot)
4470                    {
4471                        self.skip();
4472                    }
4473                    let expr_text = self.tokens_to_sql_uppercased(start, self.current);
4474                    format!("FOR SYSTEM_TIME AS OF {}", expr_text)
4475                } else {
4476                    "FOR SYSTEM_TIME AS".to_string()
4477                }
4478            } else if self.match_token(TokenType::Between) {
4479                // BETWEEN expr AND expr
4480                let start = self.current;
4481                while !self.is_at_end() && !self.check(TokenType::And) {
4482                    self.skip();
4483                }
4484                let expr1_text = self.tokens_to_sql_uppercased(start, self.current);
4485                self.skip(); // consume AND
4486                let start2 = self.current;
4487                while !self.is_at_end()
4488                    && !self.check(TokenType::Semicolon)
4489                    && !self.check(TokenType::Where)
4490                    && !self.check(TokenType::Join)
4491                    && !self.check(TokenType::Left)
4492                    && !self.check(TokenType::Right)
4493                    && !self.check(TokenType::Inner)
4494                    && !self.check(TokenType::Outer)
4495                    && !self.check(TokenType::Full)
4496                    && !self.check(TokenType::Cross)
4497                    && !self.check(TokenType::Order)
4498                    && !self.check(TokenType::Group)
4499                    && !self.check(TokenType::Having)
4500                    && !self.check(TokenType::Limit)
4501                    && !self.check(TokenType::Union)
4502                    && !self.check(TokenType::Except)
4503                    && !self.check(TokenType::Intersect)
4504                    && !self.check(TokenType::As)
4505                    && !self.check(TokenType::Comma)
4506                    && !self.check(TokenType::RParen)
4507                    && !self.check(TokenType::With)
4508                    && !self.check(TokenType::Pivot)
4509                    && !self.check(TokenType::Unpivot)
4510                {
4511                    self.skip();
4512                }
4513                let expr2_text = self.tokens_to_sql_uppercased(start2, self.current);
4514                format!("FOR SYSTEM_TIME BETWEEN {} AND {}", expr1_text, expr2_text)
4515            } else if self.match_token(TokenType::From) {
4516                // FROM expr TO expr
4517                let start = self.current;
4518                while !self.is_at_end() && !self.check(TokenType::To) {
4519                    self.skip();
4520                }
4521                let expr1_text = self.tokens_to_sql_uppercased(start, self.current);
4522                self.skip(); // consume TO
4523                let start2 = self.current;
4524                while !self.is_at_end()
4525                    && !self.check(TokenType::Semicolon)
4526                    && !self.check(TokenType::Where)
4527                    && !self.check(TokenType::As)
4528                    && !self.check(TokenType::Comma)
4529                    && !self.check(TokenType::RParen)
4530                {
4531                    self.skip();
4532                }
4533                let expr2_text = self.tokens_to_sql_uppercased(start2, self.current);
4534                format!("FOR SYSTEM_TIME FROM {} TO {}", expr1_text, expr2_text)
4535            } else if self.check_identifier("CONTAINED") {
4536                self.skip(); // consume CONTAINED
4537                self.expect(TokenType::In)?;
4538                self.expect(TokenType::LParen)?;
4539                let start = self.current;
4540                let mut depth = 1;
4541                while !self.is_at_end() && depth > 0 {
4542                    if self.check(TokenType::LParen) {
4543                        depth += 1;
4544                    }
4545                    if self.check(TokenType::RParen) {
4546                        depth -= 1;
4547                        if depth == 0 {
4548                            break;
4549                        }
4550                    }
4551                    self.skip();
4552                }
4553                let inner_text = self.tokens_to_sql_uppercased(start, self.current);
4554                self.expect(TokenType::RParen)?;
4555                format!("FOR SYSTEM_TIME CONTAINED IN ({})", inner_text)
4556            } else if self.match_token(TokenType::All) {
4557                "FOR SYSTEM_TIME ALL".to_string()
4558            } else {
4559                "FOR SYSTEM_TIME".to_string()
4560            };
4561            if let Expression::Table(ref mut table) = expr {
4562                table.system_time = Some(system_time_str);
4563            }
4564        }
4565
4566        // Check for Presto/Trino time travel: FOR VERSION AS OF / FOR TIMESTAMP AS OF
4567        // Syntax: FOR VERSION AS OF <snapshot_id>
4568        //         FOR TIMESTAMP AS OF <timestamp_expr>
4569        if self.check(TokenType::For) && self.current + 1 < self.tokens.len() {
4570            let next_text = &self.tokens[self.current + 1].text;
4571            if next_text.eq_ignore_ascii_case("VERSION") || next_text.eq_ignore_ascii_case("TIMESTAMP") {
4572                self.skip(); // consume FOR
4573                let version_kind = self.advance().text.to_ascii_uppercase(); // consume VERSION or TIMESTAMP
4574
4575                // Expect AS OF
4576                if self.match_token(TokenType::As) && self.check_keyword_text("OF") {
4577                    self.skip(); // consume OF
4578
4579                    // Parse the expression value
4580                    if let Some(value_expr) = self.parse_bitwise()? {
4581                        let version = crate::expressions::Version {
4582                            this: Box::new(Expression::Identifier(Identifier::new(&version_kind))),
4583                            kind: "AS OF".to_string(),
4584                            expression: Some(Box::new(value_expr)),
4585                        };
4586                        if let Expression::Table(ref mut table) = expr {
4587                            table.version = Some(Box::new(version));
4588                        }
4589                    }
4590                }
4591            }
4592        }
4593
4594        // Check for Hive-style time travel: TIMESTAMP AS OF / VERSION AS OF (without FOR)
4595        // Syntax: TIMESTAMP AS OF <timestamp_expr>
4596        //         VERSION AS OF <snapshot_id>
4597        if self.current < self.tokens.len() {
4598            let current_text = &self.tokens[self.current].text;
4599            if (current_text.eq_ignore_ascii_case("TIMESTAMP") || current_text.eq_ignore_ascii_case("VERSION"))
4600                && self.current + 2 < self.tokens.len()
4601                && self.tokens[self.current + 1].token_type == TokenType::As
4602                && self.tokens[self.current + 2]
4603                    .text
4604                    .eq_ignore_ascii_case("OF")
4605            {
4606                let version_kind = self.advance().text.to_ascii_uppercase(); // consume TIMESTAMP or VERSION
4607                self.skip(); // consume AS
4608                self.skip(); // consume OF
4609
4610                // Parse the expression value
4611                if let Some(value_expr) = self.parse_bitwise()? {
4612                    let version = crate::expressions::Version {
4613                        this: Box::new(Expression::Identifier(Identifier::new(&version_kind))),
4614                        kind: "AS OF".to_string(),
4615                        expression: Some(Box::new(value_expr)),
4616                    };
4617                    if let Expression::Table(ref mut table) = expr {
4618                        table.version = Some(Box::new(version));
4619                    }
4620                }
4621            }
4622        }
4623
4624        // Check for MySQL PARTITION(p0, p1, ...) clause
4625        // Only supported by MySQL-compatible dialects (not generic dialect)
4626        let supports_partition_selection = matches!(
4627            self.config.dialect,
4628            Some(crate::dialects::DialectType::MySQL)
4629                | Some(crate::dialects::DialectType::SingleStore)
4630                | Some(crate::dialects::DialectType::Doris)
4631                | Some(crate::dialects::DialectType::StarRocks)
4632        );
4633        if supports_partition_selection && self.match_token(TokenType::Partition) {
4634            if self.match_token(TokenType::LParen) {
4635                let mut partitions = Vec::new();
4636                loop {
4637                    let partition_name = self.expect_identifier_or_keyword_with_quoted()?;
4638                    partitions.push(partition_name);
4639                    if !self.match_token(TokenType::Comma) {
4640                        break;
4641                    }
4642                }
4643                self.expect(TokenType::RParen)?;
4644                if let Expression::Table(ref mut table) = expr {
4645                    table.partitions = partitions;
4646                }
4647            }
4648        }
4649
4650        // Check for table-level TABLESAMPLE/SAMPLE: tbl TABLESAMPLE METHOD(size) or tbl SAMPLE ROW(0)
4651        // Snowflake supports both TABLESAMPLE and SAMPLE
4652        if self.check(TokenType::TableSample) || self.check(TokenType::Sample) {
4653            if let Some(sample) = self.parse_table_level_sample()? {
4654                if let Expression::Table(ref mut table) = expr {
4655                    table.table_sample = Some(Box::new(sample));
4656                } else {
4657                    // For non-Table expressions (subqueries, functions, etc.),
4658                    // wrap in TableSample expression node
4659                    expr = Expression::TableSample(Box::new(crate::expressions::TableSample {
4660                        this: Some(Box::new(expr)),
4661                        sample: Some(Box::new(sample)),
4662                        expressions: Vec::new(),
4663                        method: None,
4664                        bucket_numerator: None,
4665                        bucket_denominator: None,
4666                        bucket_field: None,
4667                        percent: None,
4668                        rows: None,
4669                        size: None,
4670                        seed: None,
4671                    }));
4672                }
4673            }
4674        }
4675
4676        // Check for TSQL table hints: WITH (TABLOCK, INDEX(myindex), ...)
4677        if self.check(TokenType::With) && self.check_next(TokenType::LParen) {
4678            if let Expression::Table(ref mut table) = expr {
4679                if let Some(hint_expr) = self.parse_table_hints()? {
4680                    // parse_table_hints returns a Tuple wrapping individual hint expressions.
4681                    // Extract the inner hints so we store them directly.
4682                    match hint_expr {
4683                        Expression::Tuple(tuple) => {
4684                            table.hints = tuple.expressions;
4685                        }
4686                        other => {
4687                            table.hints = vec![other];
4688                        }
4689                    }
4690                }
4691            }
4692        }
4693
4694        // Check for MySQL index hints: USE INDEX, IGNORE INDEX, FORCE INDEX
4695        if self.check_keyword_text("USE")
4696            || self.check(TokenType::Ignore)
4697            || self.check_keyword_text("FORCE")
4698        {
4699            // Peek ahead to see if next token after USE/IGNORE/FORCE is INDEX or KEY
4700            let next_idx = self.current + 1;
4701            let is_index_hint = next_idx < self.tokens.len() && {
4702                let next_text = &self.tokens[next_idx].text;
4703                next_text.eq_ignore_ascii_case("INDEX") || next_text.eq_ignore_ascii_case("KEY")
4704            };
4705            if is_index_hint {
4706                if let Expression::Table(ref mut table) = expr {
4707                    if let Some(hint_expr) = self.parse_table_hints()? {
4708                        match hint_expr {
4709                            Expression::Tuple(tuple) => {
4710                                table.hints = tuple.expressions;
4711                            }
4712                            other => {
4713                                table.hints = vec![other];
4714                            }
4715                        }
4716                    }
4717                }
4718            }
4719        }
4720
4721        // Check for SQLite INDEXED BY or NOT INDEXED table hints
4722        if self.check_identifier("INDEXED") {
4723            self.skip(); // consume INDEXED
4724            self.expect(TokenType::By)?;
4725            // Parse index name (can be qualified: schema.index)
4726            let first_part = self.expect_identifier_or_keyword()?;
4727            let index_name = if self.match_token(TokenType::Dot) {
4728                let second_part = self.expect_identifier_or_keyword()?;
4729                format!("{}.{}", first_part, second_part)
4730            } else {
4731                first_part
4732            };
4733            if let Expression::Table(ref mut table) = expr {
4734                table.hints.push(Expression::Identifier(Identifier {
4735                    name: format!("INDEXED BY {}", index_name),
4736                    quoted: false,
4737                    trailing_comments: Vec::new(),
4738                    span: None,
4739                }));
4740            }
4741        } else if self.check(TokenType::Not) && self.check_next_identifier("INDEXED") {
4742            self.skip(); // consume NOT
4743            self.skip(); // consume INDEXED
4744            if let Expression::Table(ref mut table) = expr {
4745                table.hints.push(Expression::Identifier(Identifier {
4746                    name: "NOT INDEXED".to_string(),
4747                    quoted: false,
4748                    trailing_comments: Vec::new(),
4749                    span: None,
4750                }));
4751            }
4752        }
4753
4754        // Check for PIVOT (can be followed by UNPIVOT)
4755        // Only treat as PIVOT clause when followed by ( — otherwise it's a table alias
4756        if self.check(TokenType::Pivot) && self.check_next(TokenType::LParen) {
4757            self.skip(); // consume PIVOT
4758            expr = self.parse_pivot(expr)?;
4759        }
4760        // Check for UNPIVOT (can follow PIVOT or be standalone)
4761        // Only treat as UNPIVOT clause when followed by (, INCLUDE, or EXCLUDE — otherwise it's a table alias
4762        if self.check(TokenType::Unpivot) && self.is_unpivot_clause_start() {
4763            self.skip(); // consume UNPIVOT
4764            expr = self.parse_unpivot(expr)?;
4765        }
4766        // Check for MATCH_RECOGNIZE
4767        else if self.check(TokenType::MatchRecognize)
4768            && !matches!(&expr, Expression::Pivot(_) | Expression::Unpivot(_))
4769        {
4770            self.skip();
4771            expr = self.parse_match_recognize(Some(expr))?;
4772        }
4773
4774        // Check for alias
4775        if self.match_token(TokenType::As) {
4776            // Handle AS (col1, col2) without alias name - used by POSEXPLODE etc.
4777            if self.check(TokenType::LParen) {
4778                self.skip(); // consume LParen
4779                let mut column_aliases = Vec::new();
4780                loop {
4781                    if self.check(TokenType::RParen) {
4782                        break;
4783                    }
4784                    column_aliases.push(Identifier::new(self.expect_identifier_or_keyword()?));
4785                    if !self.match_token(TokenType::Comma) {
4786                        break;
4787                    }
4788                }
4789                self.expect(TokenType::RParen)?;
4790                expr = Expression::Alias(Box::new(Alias {
4791                    this: expr,
4792                    alias: Identifier::new(String::new()),
4793                    column_aliases,
4794                    pre_alias_comments: Vec::new(),
4795                    trailing_comments: Vec::new(),
4796                    inferred_type: None,
4797                }));
4798            } else {
4799                let alias_ident_parsed = self.expect_identifier_or_alias_keyword_with_quoted()?;
4800                let alias = alias_ident_parsed.name;
4801                let alias_is_quoted = alias_ident_parsed.quoted;
4802                let make_alias_ident = |name: String| -> Identifier {
4803                    if alias_is_quoted {
4804                        Identifier::quoted(name)
4805                    } else {
4806                        Identifier::new(name)
4807                    }
4808                };
4809                // Check for column aliases: AS t(c1, c2) or AS t(c1 type1, c2 type2) for table functions
4810                if self.match_token(TokenType::LParen) {
4811                    // Check if this is typed column definitions (for table functions like JSON_TO_RECORDSET)
4812                    // by looking ahead: if we see identifier followed by another identifier/type (not comma/rparen),
4813                    // it's typed columns
4814                    let has_typed_columns = self.check_typed_column_list();
4815
4816                    if has_typed_columns {
4817                        // Parse typed column definitions like: (col1 type1, col2 type2)
4818                        let mut typed_cols = Vec::new();
4819                        loop {
4820                            if self.check(TokenType::RParen) {
4821                                break;
4822                            }
4823                            // Parse column name (can be quoted)
4824                            let col_name = self.expect_identifier_or_keyword_with_quoted()?;
4825                            // Parse column type
4826                            let col_type = self.parse_data_type()?;
4827                            // Create ColumnDef expression, preserving the quoted status
4828                            let mut col_def = ColumnDef::new(col_name.name.clone(), col_type);
4829                            col_def.name = col_name;
4830                            typed_cols.push(Expression::ColumnDef(Box::new(col_def)));
4831
4832                            if !self.match_token(TokenType::Comma) {
4833                                break;
4834                            }
4835                        }
4836                        self.expect(TokenType::RParen)?;
4837
4838                        // Create TableAlias with typed columns
4839                        let table_alias = Expression::TableAlias(Box::new(TableAlias {
4840                            this: Some(Box::new(Expression::Identifier(make_alias_ident(alias)))),
4841                            columns: typed_cols,
4842                        }));
4843
4844                        // Wrap function with TableAlias using Tuple pattern (like ROWS FROM)
4845                        expr = Expression::Tuple(Box::new(Tuple {
4846                            expressions: vec![expr, table_alias],
4847                        }));
4848                    } else {
4849                        // Parse simple column aliases: (c1, c2, ...)
4850                        // Use expect_identifier_or_keyword to allow keywords like KEY, INDEX, VALUE as column aliases
4851                        let mut aliases = Vec::new();
4852                        loop {
4853                            if self.check(TokenType::RParen) {
4854                                break;
4855                            }
4856                            aliases.push(Identifier::new(self.expect_identifier_or_keyword()?));
4857                            if !self.match_token(TokenType::Comma) {
4858                                break;
4859                            }
4860                        }
4861                        self.expect(TokenType::RParen)?;
4862
4863                        expr = match expr {
4864                            Expression::Table(mut t) => {
4865                                t.alias = Some(make_alias_ident(alias));
4866                                t.alias_explicit_as = true;
4867                                t.column_aliases = aliases;
4868                                Expression::Table(t)
4869                            }
4870                            Expression::Subquery(mut s) => {
4871                                s.alias = Some(make_alias_ident(alias));
4872                                s.column_aliases = aliases;
4873                                Expression::Subquery(s)
4874                            }
4875                            Expression::Pivot(mut p) => {
4876                                p.alias = Some(make_alias_ident(alias));
4877                                Expression::Pivot(p)
4878                            }
4879                            Expression::Unpivot(mut u) => {
4880                                u.alias = Some(make_alias_ident(alias));
4881                                Expression::Unpivot(u)
4882                            }
4883                            Expression::MatchRecognize(mut mr) => {
4884                                mr.alias = Some(make_alias_ident(alias));
4885                                mr.alias_explicit_as = true;
4886                                Expression::MatchRecognize(mr)
4887                            }
4888                            Expression::JoinedTable(mut jt) => {
4889                                jt.alias = Some(make_alias_ident(alias));
4890                                Expression::JoinedTable(jt)
4891                            }
4892                            _ => Expression::Alias(Box::new(Alias {
4893                                this: expr,
4894                                alias: make_alias_ident(alias),
4895                                column_aliases: aliases,
4896                                pre_alias_comments: Vec::new(),
4897                                trailing_comments: Vec::new(),
4898                                inferred_type: None,
4899                            })),
4900                        };
4901                    }
4902                } else {
4903                    // No column aliases, just simple alias
4904                    let default_column_aliases = if matches!(
4905                        self.config.dialect,
4906                        Some(crate::dialects::DialectType::ClickHouse)
4907                    ) && matches!(&expr, Expression::Function(func) if func.name.eq_ignore_ascii_case("generate_series"))
4908                    {
4909                        vec![Identifier::new("generate_series")]
4910                    } else {
4911                        Vec::new()
4912                    };
4913                    expr = match expr {
4914                        Expression::Table(mut t) => {
4915                            t.alias = Some(make_alias_ident(alias));
4916                            t.alias_explicit_as = true;
4917                            t.column_aliases = Vec::new();
4918                            Expression::Table(t)
4919                        }
4920                        Expression::Subquery(mut s) => {
4921                            s.alias = Some(make_alias_ident(alias));
4922                            s.column_aliases = Vec::new();
4923                            Expression::Subquery(s)
4924                        }
4925                        Expression::Pivot(mut p) => {
4926                            p.alias = Some(make_alias_ident(alias));
4927                            Expression::Pivot(p)
4928                        }
4929                        Expression::Unpivot(mut u) => {
4930                            u.alias = Some(make_alias_ident(alias));
4931                            Expression::Unpivot(u)
4932                        }
4933                        Expression::MatchRecognize(mut mr) => {
4934                            mr.alias = Some(make_alias_ident(alias));
4935                            mr.alias_explicit_as = true;
4936                            Expression::MatchRecognize(mr)
4937                        }
4938                        Expression::JoinedTable(mut jt) => {
4939                            jt.alias = Some(make_alias_ident(alias));
4940                            Expression::JoinedTable(jt)
4941                        }
4942                        _ => Expression::Alias(Box::new(Alias {
4943                            this: expr,
4944                            alias: make_alias_ident(alias),
4945                            column_aliases: default_column_aliases,
4946                            pre_alias_comments: Vec::new(),
4947                            trailing_comments: Vec::new(),
4948                            inferred_type: None,
4949                        })),
4950                    };
4951                }
4952            } // close the else for AS (col1, col2) handling
4953        } else if (self.check(TokenType::QuotedIdentifier)
4954            || (self.check(TokenType::Var) && !self.check_keyword() && !self.check_identifier("MATCH_CONDITION")
4955                && !(self.check_identifier("ARRAY") && self.check_next(TokenType::Join)
4956                     && matches!(self.config.dialect, Some(crate::dialects::DialectType::ClickHouse)))
4957                // TSQL: OPTION(LABEL = 'foo') is a query hint, not an alias
4958                && !(self.check_identifier("OPTION") && self.check_next(TokenType::LParen))
4959                // MySQL: LOCK IN SHARE MODE is a locking clause, not an alias
4960                && !(self.check_identifier("LOCK") && self.check_next(TokenType::In))
4961                // ClickHouse: PARALLEL WITH is a statement separator, not a table alias
4962                && !(self.check_identifier("PARALLEL") && self.check_next(TokenType::With)
4963                     && matches!(self.config.dialect, Some(crate::dialects::DialectType::ClickHouse)))
4964                // DuckDB: POSITIONAL JOIN is a join method, not a table alias
4965                && !(self.check_identifier("POSITIONAL") && self.check_next(TokenType::Join))))
4966            || self.is_command_keyword_as_alias()
4967            // ClickHouse: allow FIRST/LAST as implicit table aliases
4968            // (they're keywords used in NULLS FIRST/LAST but also valid as identifiers)
4969            || (matches!(self.config.dialect, Some(crate::dialects::DialectType::ClickHouse))
4970                && (self.check(TokenType::First) || self.check(TokenType::Last)))
4971            // PIVOT/UNPIVOT can be table aliases when not followed by clause-starting tokens
4972            || (self.check(TokenType::Pivot) && !self.check_next(TokenType::LParen))
4973            || (self.check(TokenType::Unpivot) && !self.is_unpivot_clause_start())
4974            // PARTITION can be a table alias when the dialect doesn't support partition selection
4975            || (self.check(TokenType::Partition) && !matches!(
4976                self.config.dialect,
4977                Some(crate::dialects::DialectType::MySQL)
4978                | Some(crate::dialects::DialectType::SingleStore)
4979                | Some(crate::dialects::DialectType::Doris)
4980                | Some(crate::dialects::DialectType::StarRocks)
4981            ))
4982            || (self.check(TokenType::Window) && {
4983                // WINDOW can be a table alias if NOT followed by an identifier (window definition)
4984                let next_pos = self.current + 1;
4985                next_pos >= self.tokens.len()
4986                    || (self.tokens[next_pos].token_type != TokenType::Var
4987                        && self.tokens[next_pos].token_type != TokenType::Identifier)
4988            })
4989        {
4990            // Implicit alias (but not MATCH_CONDITION which is a join condition keyword)
4991            // Also allow command keywords (GET, PUT, etc.) and WINDOW (when not a clause) as implicit table aliases
4992            let is_keyword_alias = self.peek().token_type.is_keyword();
4993            let is_quoted_alias = self.peek().token_type == TokenType::QuotedIdentifier;
4994            let alias = self.advance().text.clone();
4995            // Check for column aliases: t(c1, c2)
4996            // Use expect_identifier_or_keyword to allow keywords like KEY, INDEX, VALUE as column aliases
4997            let mut column_aliases = if self.match_token(TokenType::LParen) {
4998                let mut aliases = Vec::new();
4999                loop {
5000                    aliases.push(Identifier::new(self.expect_identifier_or_keyword()?));
5001                    if !self.match_token(TokenType::Comma) {
5002                        break;
5003                    }
5004                }
5005                self.expect(TokenType::RParen)?;
5006                aliases
5007            } else {
5008                Vec::new()
5009            };
5010            if column_aliases.is_empty()
5011                && matches!(
5012                    self.config.dialect,
5013                    Some(crate::dialects::DialectType::ClickHouse)
5014                )
5015                && matches!(&expr, Expression::Function(func) if func.name.eq_ignore_ascii_case("generate_series"))
5016            {
5017                column_aliases = vec![Identifier::new("generate_series")];
5018            }
5019            let make_alias_ident = |name: String| -> Identifier {
5020                if is_quoted_alias {
5021                    Identifier::quoted(name)
5022                } else {
5023                    Identifier::new(name)
5024                }
5025            };
5026            expr = match expr {
5027                Expression::Table(mut t) => {
5028                    t.alias = Some(make_alias_ident(alias));
5029                    t.alias_explicit_as = is_keyword_alias;
5030                    t.column_aliases = column_aliases;
5031                    Expression::Table(t)
5032                }
5033                Expression::Subquery(mut s) => {
5034                    s.alias = Some(make_alias_ident(alias));
5035                    s.column_aliases = column_aliases;
5036                    Expression::Subquery(s)
5037                }
5038                Expression::Pivot(mut p) => {
5039                    p.alias = Some(make_alias_ident(alias));
5040                    Expression::Pivot(p)
5041                }
5042                Expression::Unpivot(mut u) => {
5043                    u.alias = Some(make_alias_ident(alias));
5044                    Expression::Unpivot(u)
5045                }
5046                Expression::MatchRecognize(mut mr) => {
5047                    mr.alias = Some(make_alias_ident(alias));
5048                    Expression::MatchRecognize(mr)
5049                }
5050                Expression::JoinedTable(mut jt) => {
5051                    jt.alias = Some(make_alias_ident(alias));
5052                    Expression::JoinedTable(jt)
5053                }
5054                _ => Expression::Alias(Box::new(Alias {
5055                    this: expr,
5056                    alias: make_alias_ident(alias),
5057                    column_aliases,
5058                    pre_alias_comments: Vec::new(),
5059                    trailing_comments: Vec::new(),
5060                    inferred_type: None,
5061                })),
5062            };
5063        }
5064
5065        // ClickHouse: subquery column alias list without alias name: FROM (...) (c0, c1)
5066        if matches!(
5067            self.config.dialect,
5068            Some(crate::dialects::DialectType::ClickHouse)
5069        ) && self.check(TokenType::LParen)
5070            && matches!(&expr, Expression::Subquery(s) if s.alias.is_none())
5071        {
5072            // Lookahead: check if this is (identifier, identifier, ...) — column alias list
5073            let mut look = self.current + 1;
5074            let mut is_col_list = true;
5075            let mut col_count = 0;
5076            loop {
5077                if look >= self.tokens.len() {
5078                    is_col_list = false;
5079                    break;
5080                }
5081                let tt = self.tokens[look].token_type;
5082                if tt == TokenType::Identifier
5083                    || tt == TokenType::Var
5084                    || tt == TokenType::QuotedIdentifier
5085                    || tt.is_keyword()
5086                {
5087                    col_count += 1;
5088                    look += 1;
5089                } else {
5090                    is_col_list = false;
5091                    break;
5092                }
5093                if look >= self.tokens.len() {
5094                    is_col_list = false;
5095                    break;
5096                }
5097                if self.tokens[look].token_type == TokenType::Comma {
5098                    look += 1;
5099                } else if self.tokens[look].token_type == TokenType::RParen {
5100                    break;
5101                } else {
5102                    is_col_list = false;
5103                    break;
5104                }
5105            }
5106            if is_col_list && col_count >= 1 {
5107                self.skip(); // consume LParen
5108                let mut aliases = Vec::new();
5109                loop {
5110                    aliases.push(Identifier::new(self.advance().text.clone()));
5111                    if !self.match_token(TokenType::Comma) {
5112                        break;
5113                    }
5114                }
5115                self.expect(TokenType::RParen)?;
5116                if let Expression::Subquery(ref mut s) = expr {
5117                    s.column_aliases = aliases;
5118                }
5119            }
5120        }
5121
5122        // ClickHouse FINAL modifier: table [AS alias] FINAL
5123        if matches!(
5124            self.config.dialect,
5125            Some(crate::dialects::DialectType::ClickHouse)
5126        ) && self.match_token(TokenType::Final)
5127        {
5128            if let Expression::Table(ref mut table) = expr {
5129                table.final_ = true;
5130            }
5131        }
5132
5133        // Check for SQLite INDEXED BY after alias: t AS t INDEXED BY idx
5134        if self.check_identifier("INDEXED") {
5135            self.skip(); // consume INDEXED
5136            self.expect(TokenType::By)?;
5137            let first_part = self.expect_identifier_or_keyword()?;
5138            let index_name = if self.match_token(TokenType::Dot) {
5139                let second_part = self.expect_identifier_or_keyword()?;
5140                format!("{}.{}", first_part, second_part)
5141            } else {
5142                first_part
5143            };
5144            if let Expression::Table(ref mut table) = expr {
5145                table.hints.push(Expression::Identifier(Identifier {
5146                    name: format!("INDEXED BY {}", index_name),
5147                    quoted: false,
5148                    trailing_comments: Vec::new(),
5149                    span: None,
5150                }));
5151            }
5152        }
5153
5154        // Check for PIVOT/UNPIVOT after alias (some dialects allow this order)
5155        // Only treat as PIVOT/UNPIVOT clause when followed by ( — otherwise it's a table alias
5156        if self.check(TokenType::Pivot) && self.check_next(TokenType::LParen) {
5157            self.skip(); // consume PIVOT
5158            expr = self.parse_pivot(expr)?;
5159        } else if self.check(TokenType::Unpivot) && self.is_unpivot_clause_start() {
5160            self.skip(); // consume UNPIVOT
5161            expr = self.parse_unpivot(expr)?;
5162        }
5163
5164        // Check for Redshift AT index clause for array unnesting
5165        // Syntax: table_alias.array_column AS element_alias AT index_alias
5166        // e.g., c.c_orders AS orders AT index
5167        // https://docs.aws.amazon.com/redshift/latest/dg/query-super.html
5168        if self.match_identifier("AT") {
5169            let index_alias = self.expect_identifier_or_keyword()?;
5170            // Convert the table expression to a column for AtIndex
5171            let column_expr = match expr {
5172                Expression::Table(t) => {
5173                    // Convert Table to Column reference
5174                    // For c.c_orders, table=c, name=c_orders -> column name should be c.c_orders
5175                    let mut parts = Vec::new();
5176                    if let Some(cat) = t.catalog {
5177                        parts.push(cat.name);
5178                    }
5179                    if let Some(schema) = t.schema {
5180                        parts.push(schema.name);
5181                    }
5182                    parts.push(t.name.name);
5183                    let col_name = parts.join(".");
5184                    let alias_expr = if let Some(alias) = t.alias {
5185                        Expression::Alias(Box::new(Alias {
5186                            this: Expression::boxed_column(Column {
5187                                name: Identifier::new(&col_name),
5188                                table: None,
5189                                join_mark: false,
5190                                trailing_comments: Vec::new(),
5191                                span: None,
5192                                inferred_type: None,
5193                            }),
5194                            alias,
5195                            column_aliases: t.column_aliases,
5196                            pre_alias_comments: Vec::new(),
5197                            trailing_comments: t.trailing_comments,
5198                            inferred_type: None,
5199                        }))
5200                    } else {
5201                        Expression::boxed_column(Column {
5202                            name: Identifier::new(&col_name),
5203                            table: None,
5204                            join_mark: false,
5205                            trailing_comments: t.trailing_comments,
5206                            span: None,
5207                            inferred_type: None,
5208                        })
5209                    };
5210                    alias_expr
5211                }
5212                other => other, // Keep as is for non-table expressions
5213            };
5214            expr = Expression::AtIndex(Box::new(AtIndex {
5215                this: Box::new(column_expr),
5216                expression: Box::new(Expression::Identifier(Identifier::new(index_alias))),
5217            }));
5218        }
5219
5220        // Check for TABLESAMPLE/SAMPLE after alias (Snowflake ALIAS_POST_TABLESAMPLE)
5221        // e.g., table2 AS t2 TABLESAMPLE BERNOULLI (50), table2 AS t2 SAMPLE ROW (0)
5222        if self.check(TokenType::TableSample) || self.check(TokenType::Sample) {
5223            if let Some(sample) = self.parse_table_level_sample()? {
5224                // Capture trailing comments after the SAMPLE clause (e.g., -- 25% of rows in table1)
5225                let post_sample_comments = self.previous_trailing_comments().to_vec();
5226                if let Expression::Table(ref mut table) = expr {
5227                    table.table_sample = Some(Box::new(sample));
5228                    if !post_sample_comments.is_empty() {
5229                        table.trailing_comments.extend(post_sample_comments);
5230                    }
5231                } else {
5232                    // For non-Table expressions, wrap in TableSample expression node
5233                    expr = Expression::TableSample(Box::new(crate::expressions::TableSample {
5234                        this: Some(Box::new(expr)),
5235                        sample: Some(Box::new(sample)),
5236                        expressions: Vec::new(),
5237                        method: None,
5238                        bucket_numerator: None,
5239                        bucket_denominator: None,
5240                        bucket_field: None,
5241                        percent: None,
5242                        rows: None,
5243                        size: None,
5244                        seed: None,
5245                    }));
5246                }
5247            }
5248        }
5249
5250        // Apply PostgreSQL ONLY modifier if present
5251        if has_only {
5252            if let Expression::Table(ref mut table) = expr {
5253                table.only = true;
5254            }
5255        }
5256
5257        // BigQuery: FOR SYSTEM_TIME AS OF after alias
5258        // e.g., FROM foo AS t0 FOR SYSTEM_TIME AS OF '2026-01-01'
5259        if self.check(TokenType::For)
5260            && self.current + 1 < self.tokens.len()
5261            && self.tokens[self.current + 1]
5262                .text
5263                .eq_ignore_ascii_case("SYSTEM_TIME")
5264        {
5265            self.skip(); // consume FOR
5266            self.skip(); // consume SYSTEM_TIME
5267            if self.match_token(TokenType::As) && self.check_keyword_text("OF") {
5268                self.skip(); // consume OF
5269                let start = self.current;
5270                // Collect expression tokens until clause boundary
5271                while !self.is_at_end()
5272                    && !self.check(TokenType::Semicolon)
5273                    && !self.check(TokenType::Where)
5274                    && !self.check(TokenType::Join)
5275                    && !self.check(TokenType::Left)
5276                    && !self.check(TokenType::Right)
5277                    && !self.check(TokenType::Inner)
5278                    && !self.check(TokenType::Outer)
5279                    && !self.check(TokenType::Full)
5280                    && !self.check(TokenType::Cross)
5281                    && !self.check(TokenType::Order)
5282                    && !self.check(TokenType::Group)
5283                    && !self.check(TokenType::Having)
5284                    && !self.check(TokenType::Limit)
5285                    && !self.check(TokenType::Union)
5286                    && !self.check(TokenType::Except)
5287                    && !self.check(TokenType::Intersect)
5288                    && !self.check(TokenType::Comma)
5289                    && !self.check(TokenType::RParen)
5290                {
5291                    self.skip();
5292                }
5293                let expr_text = self.tokens_to_sql(start, self.current);
5294                let system_time_str = format!("FOR SYSTEM_TIME AS OF {}", expr_text);
5295                if let Expression::Table(ref mut table) = expr {
5296                    table.system_time = Some(system_time_str);
5297                }
5298            }
5299        }
5300
5301        // BigQuery INFORMATION_SCHEMA handling
5302        // When INFORMATION_SCHEMA is part of a table reference, merge it with the table name
5303        // into a single quoted identifier and auto-add an alias if not present
5304        if matches!(
5305            self.config.dialect,
5306            Some(crate::dialects::DialectType::BigQuery)
5307        ) {
5308            if let Expression::Table(ref mut table) = expr {
5309                // Case 1: Single quoted identifier containing INFORMATION_SCHEMA (e.g., `proj.dataset.INFORMATION_SCHEMA.SOME_VIEW`)
5310                // Add an alias that is the same as the table name (only if no alias)
5311                if table.schema.is_none() && table.catalog.is_none() && table.alias.is_none() {
5312                    let name_upper = table.name.name.to_ascii_uppercase();
5313                    if name_upper.contains("INFORMATION_SCHEMA.") {
5314                        // Set alias to be the full quoted table name
5315                        table.alias = Some(table.name.clone());
5316                        table.alias_explicit_as = true;
5317                    }
5318                }
5319                // Case 2: Multi-part name where schema part is INFORMATION_SCHEMA
5320                // e.g., region_or_dataset.INFORMATION_SCHEMA.TABLES -> region_or_dataset.`INFORMATION_SCHEMA.TABLES` AS TABLES
5321                // e.g., proj.region_or_dataset.INFORMATION_SCHEMA.TABLES -> proj.region_or_dataset.`INFORMATION_SCHEMA.TABLES` AS TABLES
5322                // This applies even if an alias is already set (we still need to merge the parts)
5323                else if let Some(ref schema) = table.schema {
5324                    if schema.name.eq_ignore_ascii_case("INFORMATION_SCHEMA") {
5325                        // Merge schema (INFORMATION_SCHEMA) with table name into a single quoted identifier
5326                        let merged_name = format!("{}.{}", schema.name, table.name.name);
5327                        let original_table_name = table.name.name.clone();
5328
5329                        // Set alias to original table name (TABLES, VIEWS, etc.) only if no alias exists
5330                        if table.alias.is_none() {
5331                            table.alias = Some(Identifier::new(original_table_name));
5332                            table.alias_explicit_as = true;
5333                        }
5334
5335                        // Create new quoted identifier
5336                        table.name = Identifier {
5337                            name: merged_name,
5338                            quoted: true,
5339                            trailing_comments: Vec::new(),
5340                            span: None,
5341                        };
5342
5343                        // Shift: schema becomes catalog, catalog becomes None or stays
5344                        table.schema = table.catalog.take();
5345                        // catalog is now None
5346                    }
5347                }
5348            }
5349        }
5350
5351        Ok(expr)
5352    }
5353
5354    /// Parse standard PIVOT clause (in FROM clause)
5355    /// PIVOT(agg_func [AS alias], ... FOR column IN (value [AS alias], ...) [GROUP BY ...])
5356    fn parse_pivot(&mut self, source: Expression) -> Result<Expression> {
5357        self.expect(TokenType::LParen)?;
5358
5359        // Parse aggregation functions (comma-separated, may have aliases)
5360        // Stop when we see FOR keyword
5361        // Use parse_primary() to handle keyword function names like FIRST, LAST
5362        let mut expressions = Vec::new();
5363        loop {
5364            if self.check(TokenType::For) || self.check(TokenType::RParen) {
5365                break;
5366            }
5367            // Parse the aggregation expression using parse_primary (handles keyword functions)
5368            let func = self.parse_primary()?;
5369            // Check for alias (AS alias or just identifier after function)
5370            let expr = if self.match_token(TokenType::As) {
5371                // AS alias
5372                let alias_name = self.expect_identifier_or_keyword()?;
5373                Expression::Alias(Box::new(Alias::new(func, Identifier::new(alias_name))))
5374            } else if !self.check(TokenType::Comma)
5375                && !self.check(TokenType::For)
5376                && !self.check(TokenType::RParen)
5377            {
5378                // Implicit alias (no AS keyword): SUM(b) d
5379                if let Some(id) = self.parse_id_var()? {
5380                    let alias_name = match &id {
5381                        Expression::Identifier(ident) => ident.name.clone(),
5382                        Expression::Column(col) => col.name.name.clone(),
5383                        _ => String::new(),
5384                    };
5385                    if !alias_name.is_empty() {
5386                        Expression::Alias(Box::new(Alias::new(func, Identifier::new(alias_name))))
5387                    } else {
5388                        func
5389                    }
5390                } else {
5391                    func
5392                }
5393            } else {
5394                func
5395            };
5396            expressions.push(expr);
5397            if !self.match_token(TokenType::Comma) {
5398                break;
5399            }
5400            // After consuming comma, if next is FOR, break (comma before FOR is optional/dropped)
5401            if self.check(TokenType::For) {
5402                break;
5403            }
5404        }
5405
5406        // FOR column IN (values)
5407        self.expect(TokenType::For)?;
5408
5409        let mut fields = Vec::new();
5410        loop {
5411            let field = self.parse_standard_pivot_in()?;
5412            fields.push(field);
5413
5414            // Check for additional FOR clauses (rare but possible)
5415            if !self.match_token(TokenType::For) {
5416                break;
5417            }
5418        }
5419
5420        // Handle Snowflake's DEFAULT ON NULL (default_value) clause
5421        let default_on_null = if self.match_text_seq(&["DEFAULT", "ON", "NULL"]) {
5422            if self.match_token(TokenType::LParen) {
5423                let val = self.parse_expression()?;
5424                self.expect(TokenType::RParen)?;
5425                Some(Box::new(val))
5426            } else {
5427                None
5428            }
5429        } else {
5430            None
5431        };
5432
5433        // Parse optional GROUP BY inside PIVOT parens
5434        let group = self.parse_group()?;
5435
5436        self.expect(TokenType::RParen)?;
5437
5438        Ok(Expression::Pivot(Box::new(Pivot {
5439            this: source,
5440            expressions,
5441            fields,
5442            using: Vec::new(),
5443            group: group.map(Box::new),
5444            unpivot: false,
5445            into: None,
5446            alias: None,
5447            include_nulls: None,
5448            default_on_null,
5449            with: None,
5450        })))
5451    }
5452
5453    /// Parse FOR column IN (...) part of standard PIVOT
5454    fn parse_standard_pivot_in(&mut self) -> Result<Expression> {
5455        // Parse the column being pivoted
5456        let column = self.parse_primary()?;
5457
5458        // IN keyword
5459        self.expect(TokenType::In)?;
5460
5461        // IN values - can be parenthesized or bare identifier
5462        if self.match_token(TokenType::LParen) {
5463            // Check for ANY keyword
5464            let in_exprs = if self.match_text_seq(&["ANY"]) {
5465                let order = self.parse_order()?;
5466                vec![Expression::PivotAny(Box::new(PivotAny {
5467                    this: order.map(Box::new),
5468                }))]
5469            } else {
5470                // Parse comma-separated values with optional aliases
5471                let mut vals = Vec::new();
5472                loop {
5473                    if self.check(TokenType::RParen) {
5474                        break;
5475                    }
5476                    if let Some(val) = self.parse_select_or_expression()? {
5477                        // Check for alias - alias can be an identifier or an expression
5478                        // (e.g., 'PREFIX ' || CHR(38) || ' SUFFIX' in Oracle)
5479                        let val = if self.match_token(TokenType::As) {
5480                            // Parse the alias as an expression (not just an identifier)
5481                            // This allows for string concatenation aliases
5482                            let alias_expr = self.parse_bitwise()?.ok_or_else(|| {
5483                                self.parse_error(
5484                                    "Expected expression after AS in PIVOT/UNPIVOT IN clause",
5485                                )
5486                            })?;
5487                            Expression::PivotAlias(Box::new(PivotAlias {
5488                                this: val,
5489                                alias: alias_expr,
5490                            }))
5491                        } else {
5492                            val
5493                        };
5494                        vals.push(val);
5495                    }
5496                    if !self.match_token(TokenType::Comma) {
5497                        break;
5498                    }
5499                }
5500                vals
5501            };
5502            self.expect(TokenType::RParen)?;
5503            Ok(Expression::In(Box::new(In {
5504                this: column,
5505                expressions: in_exprs,
5506                query: None,
5507                not: false,
5508                global: false,
5509                unnest: None,
5510                is_field: false,
5511            })))
5512        } else {
5513            // Bare identifier: FOR foo IN y_enum (no parentheses)
5514            // Store in query field to distinguish from parenthesized IN
5515            let field_id = self.parse_id_var()?.unwrap_or(Expression::Null(Null));
5516            Ok(Expression::In(Box::new(In {
5517                this: column,
5518                expressions: Vec::new(),
5519                query: Some(field_id),
5520                not: false,
5521                global: false,
5522                unnest: None,
5523                is_field: true,
5524            })))
5525        }
5526    }
5527
5528    /// Parse UNPIVOT clause
5529    /// UNPIVOT (value_column FOR name_column IN (col1, col2, ...))
5530    /// UNPIVOT ((col1, col2) FOR name_column IN (col1, col2, ...))
5531    /// UNPIVOT INCLUDE NULLS (value_column FOR name_column IN (...))
5532    /// UNPIVOT EXCLUDE NULLS (value_column FOR name_column IN (...))
5533    fn parse_unpivot(&mut self, source: Expression) -> Result<Expression> {
5534        // Check for optional INCLUDE NULLS or EXCLUDE NULLS
5535        let include_nulls = if self.match_text_seq(&["INCLUDE", "NULLS"]) {
5536            Some(true)
5537        } else if self.match_text_seq(&["EXCLUDE", "NULLS"]) {
5538            Some(false)
5539        } else {
5540            None
5541        };
5542
5543        self.expect(TokenType::LParen)?;
5544
5545        // Value column(s) - can be identifier or (col1, col2, ...)
5546        // Allow keywords as identifiers (e.g., "values" is a common column name in UNPIVOT)
5547        let (value_column, value_column_parenthesized, extra_value_columns) =
5548            if self.match_token(TokenType::LParen) {
5549                // Parenthesized value column(s)
5550                let col = self.expect_identifier_or_keyword()?;
5551                let mut extra_cols = Vec::new();
5552                while self.match_token(TokenType::Comma) {
5553                    extra_cols.push(Identifier::new(self.expect_identifier_or_keyword()?));
5554                }
5555                self.expect(TokenType::RParen)?;
5556                (Identifier::new(col), true, extra_cols)
5557            } else {
5558                (
5559                    Identifier::new(self.expect_identifier_or_keyword()?),
5560                    false,
5561                    Vec::new(),
5562                )
5563            };
5564
5565        // FOR name_column
5566        self.expect(TokenType::For)?;
5567        let name_column = Identifier::new(self.expect_identifier_or_keyword()?);
5568
5569        // IN (columns with optional aliases)
5570        // Format: col1 [AS alias1], col2 [AS alias2], ...
5571        // Or tuple format: (col1, col2) [AS alias1], (col3, col4) [AS alias2], ...
5572        // Aliases can be expressions like 'PREFIX ' || CHR(38) || ' SUFFIX'
5573        self.expect(TokenType::In)?;
5574        self.expect(TokenType::LParen)?;
5575        let columns = {
5576            let mut cols = Vec::new();
5577            loop {
5578                if self.check(TokenType::RParen) {
5579                    break;
5580                }
5581                // Check if this is a tuple of columns: (col1, col2)
5582                let col_expr = if self.check(TokenType::LParen) {
5583                    // Could be a tuple of columns for multi-value unpivot
5584                    let saved = self.current;
5585                    self.skip(); // consume (
5586                                    // Try parsing as identifier list (tuple of columns)
5587                    let mut tuple_cols = Vec::new();
5588                    let first = self.expect_identifier_or_keyword();
5589                    if let Ok(first_id) = first {
5590                        tuple_cols.push(Expression::column(first_id));
5591                        while self.match_token(TokenType::Comma) {
5592                            if let Ok(id) = self.expect_identifier_or_keyword() {
5593                                tuple_cols.push(Expression::column(id));
5594                            } else {
5595                                break;
5596                            }
5597                        }
5598                        if self.match_token(TokenType::RParen) && tuple_cols.len() > 1 {
5599                            // Successful tuple parse
5600                            Some(Expression::Tuple(Box::new(Tuple {
5601                                expressions: tuple_cols,
5602                            })))
5603                        } else {
5604                            // Not a tuple, backtrack
5605                            self.current = saved;
5606                            self.parse_select_or_expression()?
5607                        }
5608                    } else {
5609                        // Not an identifier, backtrack
5610                        self.current = saved;
5611                        self.parse_select_or_expression()?
5612                    }
5613                } else {
5614                    self.parse_select_or_expression()?
5615                };
5616
5617                if let Some(col) = col_expr {
5618                    // Check for alias
5619                    let col = if self.match_token(TokenType::As) {
5620                        // Parse the alias as an expression (allows string concatenation)
5621                        let alias_expr = self.parse_bitwise()?.ok_or_else(|| {
5622                            self.parse_error("Expected expression after AS in UNPIVOT IN clause")
5623                        })?;
5624                        Expression::PivotAlias(Box::new(PivotAlias {
5625                            this: col,
5626                            alias: alias_expr,
5627                        }))
5628                    } else {
5629                        col
5630                    };
5631                    cols.push(col);
5632                }
5633                if !self.match_token(TokenType::Comma) {
5634                    break;
5635                }
5636            }
5637            cols
5638        };
5639        self.expect(TokenType::RParen)?;
5640
5641        self.expect(TokenType::RParen)?;
5642
5643        Ok(Expression::Unpivot(Box::new(Unpivot {
5644            this: source,
5645            value_column,
5646            name_column,
5647            columns,
5648            alias: None,
5649            value_column_parenthesized,
5650            include_nulls,
5651            extra_value_columns,
5652        })))
5653    }
5654
5655    /// Parse Redshift UNPIVOT in FROM clause for SUPER object traversal
5656    /// Syntax: UNPIVOT expr [AS val_alias AT attr_alias]
5657    /// Examples:
5658    ///   FROM t, UNPIVOT t.arr[0]
5659    ///   FROM t, UNPIVOT t.arr AS val AT attr
5660    fn parse_redshift_unpivot_table(&mut self) -> Result<Expression> {
5661        // Parse the expression (column reference with possible array subscript)
5662        // We need to parse a primary expression that can include:
5663        // - Simple column: c.c_orders
5664        // - Array subscript: c.c_orders[0]
5665        // - Multiple subscripts: c.c_orders[0].items[1]
5666        // Using parse_primary which handles column refs with subscripts
5667        let this = self.parse_primary()?;
5668
5669        // Check for optional AS val_alias AT attr_alias
5670        let alias = if self.match_token(TokenType::As) {
5671            let val_alias = self.expect_identifier_or_keyword()?;
5672            // Check for AT attr_alias
5673            if self.match_text_seq(&["AT"]) {
5674                let attr_alias = self.expect_identifier_or_keyword()?;
5675                // Create alias expression that captures both aliases
5676                // We'll use the val_alias as the main alias and store attr_alias in a way
5677                // the generator can reconstruct "AS val AT attr"
5678                Some(Identifier::new(format!("{} AT {}", val_alias, attr_alias)))
5679            } else {
5680                Some(Identifier::new(val_alias))
5681            }
5682        } else {
5683            None
5684        };
5685
5686        // Return a Pivot expression with unpivot=true
5687        // Use the simplified form pattern where:
5688        // - this: the expression being unpivoted
5689        // - expressions: empty (no ON expressions)
5690        // - unpivot: true
5691        // - alias: captured above
5692        Ok(Expression::Pivot(Box::new(Pivot {
5693            this,
5694            expressions: Vec::new(),
5695            fields: Vec::new(),
5696            using: Vec::new(),
5697            group: None,
5698            unpivot: true,
5699            into: None,
5700            alias,
5701            include_nulls: None,
5702            default_on_null: None,
5703            with: None,
5704        })))
5705    }
5706
5707    /// BigQuery: Parse a table part that may contain hyphens (e.g., project-id)
5708    /// Also handles numeric table parts (e.g., foo.bar.25 -> foo.bar.`25`)
5709    /// Returns the identifier, possibly with merged hyphenated parts and quoted flag set.
5710    fn parse_bigquery_table_part(&mut self) -> Result<Identifier> {
5711        use crate::dialects::DialectType;
5712
5713        // Try to parse a number for BigQuery numeric table parts (e.g., foo.bar.25)
5714        if matches!(self.config.dialect, Some(DialectType::BigQuery))
5715            && self.check(TokenType::Number)
5716        {
5717            let num_token = self.advance().clone();
5718            let mut name = num_token.text.clone();
5719
5720            // Check if followed by more connected tokens (e.g., 25x, 25_, 25ab)
5721            // Numbers followed immediately by identifiers without whitespace are merged
5722            while !self.is_at_end() && self.is_connected() {
5723                let tok = self.advance().clone();
5724                name.push_str(&tok.text);
5725            }
5726
5727            return Ok(Identifier {
5728                name,
5729                quoted: true,
5730                trailing_comments: Vec::new(),
5731                span: None,
5732            });
5733        }
5734
5735        // MySQL numeric-starting identifiers (e.g., 00f, 1d)
5736        if matches!(self.config.dialect, Some(DialectType::MySQL)) && self.check(TokenType::Number)
5737        {
5738            let num_token = self.advance().clone();
5739            let mut name = num_token.text.clone();
5740
5741            // Merge with connected identifier/var tokens only (not punctuation)
5742            while !self.is_at_end()
5743                && self.is_connected()
5744                && (self.check(TokenType::Var) || self.check(TokenType::Identifier))
5745            {
5746                let tok = self.advance().clone();
5747                name.push_str(&tok.text);
5748            }
5749
5750            return Ok(Identifier {
5751                name,
5752                quoted: true,
5753                trailing_comments: Vec::new(),
5754                span: None,
5755            });
5756        }
5757
5758        let mut ident = self.expect_identifier_or_keyword_with_quoted()?;
5759
5760        // BigQuery: merge hyphenated parts (e.g., pro-ject_id -> `pro-ject_id`)
5761        if matches!(self.config.dialect, Some(DialectType::BigQuery)) && !ident.quoted {
5762            // Check if next token is a dash and it looks connected (no space)
5763            if self.check(TokenType::Dash) && self.is_connected_dash() {
5764                let mut name = ident.name.clone();
5765
5766                while self.check(TokenType::Dash) && self.is_connected_dash() {
5767                    self.skip(); // consume dash
5768                    name.push('-');
5769                    // Consume the next part
5770                    let part = self.advance().clone();
5771                    name.push_str(&part.text);
5772                    // Continue consuming connected tokens (for things like a-b-c)
5773                    while !self.is_at_end()
5774                        && self.is_connected()
5775                        && !self.check(TokenType::Dot)
5776                        && !self.check(TokenType::Dash)
5777                        && !self.check(TokenType::LParen)
5778                        && !self.check(TokenType::RParen)
5779                    {
5780                        let tok = self.advance().clone();
5781                        name.push_str(&tok.text);
5782                    }
5783                }
5784
5785                ident = Identifier {
5786                    name,
5787                    quoted: false,
5788                    trailing_comments: Vec::new(),
5789                    span: None,
5790                };
5791            }
5792        }
5793
5794        Ok(ident)
5795    }
5796
5797    /// Check if the current dash token is "connected" to the next token
5798    /// (i.e., the dash and next token are part of a hyphenated identifier)
5799    fn is_connected_dash(&self) -> bool {
5800        if !self.check(TokenType::Dash) {
5801            return false;
5802        }
5803        if self.current + 1 >= self.tokens.len() {
5804            return false;
5805        }
5806        let dash_token = &self.tokens[self.current];
5807        let next_token = &self.tokens[self.current + 1];
5808
5809        // The next token after dash must be an identifier, number, or keyword
5810        // and it must be adjacent (no whitespace between dash and next token)
5811        let next_is_valid = matches!(
5812            next_token.token_type,
5813            TokenType::Identifier
5814                | TokenType::Var
5815                | TokenType::Number
5816                | TokenType::All
5817                | TokenType::Select
5818                | TokenType::From
5819                | TokenType::Where
5820        ) || next_token.token_type.is_keyword();
5821
5822        // Check adjacency: dash ends at dash.end, next starts at next.start
5823        let adjacent = dash_token.span.end + 1 == next_token.span.start
5824            || dash_token.span.end == next_token.span.start;
5825
5826        next_is_valid && adjacent
5827    }
5828
5829    /// Check if the current token is "connected" to the previous token (no whitespace)
5830    fn is_connected(&self) -> bool {
5831        if self.current == 0 || self.current >= self.tokens.len() {
5832            return false;
5833        }
5834        let prev_token = &self.tokens[self.current - 1];
5835        let curr_token = &self.tokens[self.current];
5836        // Tokens are connected if they are immediately adjacent (no characters between them)
5837        // span.end is exclusive, so if prev.end == curr.start, they are adjacent
5838        prev_token.span.end == curr_token.span.start
5839    }
5840
5841    /// Parse a table reference (schema.table format)
5842    fn parse_table_ref(&mut self) -> Result<TableRef> {
5843        // Check for Snowflake IDENTIFIER() function: IDENTIFIER('string') or IDENTIFIER($var)
5844        if self.check_identifier("IDENTIFIER") && self.check_next(TokenType::LParen) {
5845            self.skip(); // consume IDENTIFIER
5846            self.skip(); // consume (
5847                            // Parse the argument: either a string literal, a variable ($foo), or identifier
5848            let arg = if self.check(TokenType::String) {
5849                let s = self.advance().text.clone();
5850                Expression::Literal(Literal::String(s))
5851            } else if self.check(TokenType::Parameter) {
5852                // ?-style parameter
5853                let var = self.advance().text.clone();
5854                Expression::Var(Box::new(crate::expressions::Var { this: var }))
5855            } else if self.check(TokenType::Dollar) {
5856                // $foo style variable - Dollar followed by identifier
5857                self.skip(); // consume $
5858                let var_name = self.expect_identifier()?;
5859                Expression::Var(Box::new(crate::expressions::Var {
5860                    this: format!("${}", var_name),
5861                }))
5862            } else {
5863                // Could be an identifier too
5864                let ident = self.expect_identifier()?;
5865                Expression::Identifier(Identifier::new(ident))
5866            };
5867            self.expect(TokenType::RParen)?;
5868            let trailing_comments = self.previous_trailing_comments().to_vec();
5869            // Create a Function expression to represent IDENTIFIER(arg)
5870            let identifier_func = Expression::Function(Box::new(crate::expressions::Function {
5871                name: "IDENTIFIER".to_string(),
5872                args: vec![arg],
5873                distinct: false,
5874                trailing_comments: Vec::new(),
5875                use_bracket_syntax: false,
5876                no_parens: false,
5877                quoted: false,
5878                span: None,
5879                inferred_type: None,
5880            }));
5881            return Ok(TableRef {
5882                catalog: None,
5883                schema: None,
5884                name: Identifier::empty(),
5885                alias: None,
5886                alias_explicit_as: false,
5887                column_aliases: Vec::new(),
5888                trailing_comments,
5889                when: None,
5890                only: false,
5891                final_: false,
5892                table_sample: None,
5893                hints: Vec::new(),
5894                system_time: None,
5895                partitions: Vec::new(),
5896                identifier_func: Some(Box::new(identifier_func)),
5897                changes: None,
5898                version: None,
5899                span: None,
5900            });
5901        }
5902
5903        let first = self.parse_bigquery_table_part()?;
5904
5905        // Check for schema.table format
5906        if self.match_token(TokenType::Dot) {
5907            // Handle TSQL a..b syntax (database..table with empty schema)
5908            if self.check(TokenType::Dot) {
5909                // Two consecutive dots: a..b means catalog..table (empty schema)
5910                self.skip(); // consume second dot
5911                let table = self.parse_bigquery_table_part()?;
5912                let trailing_comments = self.previous_trailing_comments().to_vec();
5913                Ok(TableRef {
5914                    catalog: Some(first),
5915                    schema: Some(Identifier::new("")), // Empty schema represents ..
5916                    name: table,
5917                    alias: None,
5918                    alias_explicit_as: false,
5919                    column_aliases: Vec::new(),
5920                    trailing_comments,
5921                    when: None,
5922                    only: false,
5923                    final_: false,
5924                    table_sample: None,
5925                    hints: Vec::new(),
5926                    system_time: None,
5927                    partitions: Vec::new(),
5928                    identifier_func: None,
5929                    changes: None,
5930                    version: None,
5931                    span: None,
5932                })
5933            } else {
5934                // BigQuery: handle x.* wildcard table reference (e.g., SELECT * FROM x.*)
5935                // After the first dot, if we see a Star token, it's a wildcard table name
5936                if matches!(
5937                    self.config.dialect,
5938                    Some(crate::dialects::DialectType::BigQuery)
5939                ) && self.check(TokenType::Star)
5940                {
5941                    self.skip(); // consume *
5942                    let trailing_comments = self.previous_trailing_comments().to_vec();
5943                    return Ok(TableRef {
5944                        catalog: None,
5945                        schema: Some(first),
5946                        name: Identifier::new("*"),
5947                        alias: None,
5948                        alias_explicit_as: false,
5949                        column_aliases: Vec::new(),
5950                        trailing_comments,
5951                        when: None,
5952                        only: false,
5953                        final_: false,
5954                        table_sample: None,
5955                        hints: Vec::new(),
5956                        system_time: None,
5957                        partitions: Vec::new(),
5958                        identifier_func: None,
5959                        changes: None,
5960                        version: None,
5961                        span: None,
5962                    });
5963                }
5964                let table = self.parse_bigquery_table_part()?;
5965                // Check for catalog.schema.table format
5966                if self.match_token(TokenType::Dot) {
5967                    // BigQuery: handle a.b.* wildcard table reference
5968                    if matches!(
5969                        self.config.dialect,
5970                        Some(crate::dialects::DialectType::BigQuery)
5971                    ) && self.check(TokenType::Star)
5972                    {
5973                        self.skip(); // consume *
5974                        let trailing_comments = self.previous_trailing_comments().to_vec();
5975                        return Ok(TableRef {
5976                            catalog: Some(first),
5977                            schema: Some(table),
5978                            name: Identifier::new("*"),
5979                            alias: None,
5980                            alias_explicit_as: false,
5981                            column_aliases: Vec::new(),
5982                            trailing_comments,
5983                            when: None,
5984                            only: false,
5985                            final_: false,
5986                            table_sample: None,
5987                            hints: Vec::new(),
5988                            system_time: None,
5989                            partitions: Vec::new(),
5990                            identifier_func: None,
5991                            changes: None,
5992                            version: None,
5993                            span: None,
5994                        });
5995                    }
5996                    let actual_table = self.parse_bigquery_table_part()?;
5997                    let trailing_comments = self.previous_trailing_comments().to_vec();
5998                    Ok(TableRef {
5999                        catalog: Some(first),
6000                        schema: Some(table),
6001                        name: actual_table,
6002                        alias: None,
6003                        alias_explicit_as: false,
6004                        column_aliases: Vec::new(),
6005                        trailing_comments,
6006                        when: None,
6007                        only: false,
6008                        final_: false,
6009                        table_sample: None,
6010                        hints: Vec::new(),
6011                        system_time: None,
6012                        partitions: Vec::new(),
6013                        identifier_func: None,
6014                        changes: None,
6015                        version: None,
6016                        span: None,
6017                    })
6018                } else {
6019                    let trailing_comments = self.previous_trailing_comments().to_vec();
6020                    Ok(TableRef {
6021                        catalog: None,
6022                        schema: Some(first),
6023                        name: table,
6024                        alias: None,
6025                        alias_explicit_as: false,
6026                        column_aliases: Vec::new(),
6027                        trailing_comments,
6028                        when: None,
6029                        only: false,
6030                        final_: false,
6031                        table_sample: None,
6032                        hints: Vec::new(),
6033                        system_time: None,
6034                        partitions: Vec::new(),
6035                        identifier_func: None,
6036                        changes: None,
6037                        version: None,
6038                        span: None,
6039                    })
6040                }
6041            }
6042        } else {
6043            let trailing_comments = self.previous_trailing_comments().to_vec();
6044            Ok(TableRef {
6045                catalog: None,
6046                schema: None,
6047                name: first,
6048                alias: None,
6049                alias_explicit_as: false,
6050                column_aliases: Vec::new(),
6051                trailing_comments,
6052                when: None,
6053                only: false,
6054                final_: false,
6055                table_sample: None,
6056                hints: Vec::new(),
6057                system_time: None,
6058                partitions: Vec::new(),
6059                identifier_func: None,
6060                changes: None,
6061                version: None,
6062                span: None,
6063            })
6064        }
6065    }
6066
6067    /// Parse a datetime field for EXTRACT function (YEAR, MONTH, DAY, etc.)
6068    fn parse_datetime_field(&mut self) -> Result<DateTimeField> {
6069        let token = self.advance();
6070        let original_name = token.text.clone();
6071        let name = original_name.to_ascii_uppercase();
6072        match name.as_str() {
6073            "YEAR" => Ok(DateTimeField::Year),
6074            "MONTH" => Ok(DateTimeField::Month),
6075            "DAY" => Ok(DateTimeField::Day),
6076            "HOUR" => Ok(DateTimeField::Hour),
6077            "MINUTE" => Ok(DateTimeField::Minute),
6078            "SECOND" => Ok(DateTimeField::Second),
6079            "MILLISECOND" => Ok(DateTimeField::Millisecond),
6080            "MICROSECOND" => Ok(DateTimeField::Microsecond),
6081            "DOW" | "DAYOFWEEK" => Ok(DateTimeField::DayOfWeek),
6082            "DOY" | "DAYOFYEAR" => Ok(DateTimeField::DayOfYear),
6083            "WEEK" => {
6084                // Check for modifier like WEEK(monday)
6085                if self.match_token(TokenType::LParen) {
6086                    let modifier = self.expect_identifier_or_keyword()?;
6087                    self.expect(TokenType::RParen)?;
6088                    Ok(DateTimeField::WeekWithModifier(modifier))
6089                } else {
6090                    Ok(DateTimeField::Week)
6091                }
6092            }
6093            "QUARTER" => Ok(DateTimeField::Quarter),
6094            "EPOCH" => Ok(DateTimeField::Epoch),
6095            "TIMEZONE" => Ok(DateTimeField::Timezone),
6096            "TIMEZONE_HOUR" => Ok(DateTimeField::TimezoneHour),
6097            "TIMEZONE_MINUTE" => Ok(DateTimeField::TimezoneMinute),
6098            "DATE" => Ok(DateTimeField::Date),
6099            "TIME" => Ok(DateTimeField::Time),
6100            // Allow arbitrary field names for dialect-specific functionality
6101            _ => Ok(DateTimeField::Custom(original_name)),
6102        }
6103    }
6104
6105    /// Parse a table expression followed by any joins
6106    /// Used for parenthesized join expressions like (tbl1 CROSS JOIN tbl2)
6107    fn parse_table_expression_with_joins(&mut self) -> Result<(Expression, Vec<Join>)> {
6108        // First parse the left table expression
6109        let left = self.parse_table_expression()?;
6110
6111        // Then parse any joins
6112        let joins = self.parse_joins()?;
6113
6114        Ok((left, joins))
6115    }
6116
6117    /// Parse JOIN clauses
6118    ///
6119    /// Supports right-associative chained JOINs where ON/USING clauses are assigned right-to-left:
6120    /// - `a JOIN b JOIN c ON cond1 ON cond2` means `a JOIN (b JOIN c ON cond1) ON cond2`
6121    /// - The rightmost ON applies to the rightmost unconditioned JOIN
6122    fn parse_joins(&mut self) -> Result<Vec<Join>> {
6123        let mut joins = Vec::new();
6124        let mut nesting_group: usize = 0;
6125
6126        // Loop: Phase 1 (parse JOINs) + Phase 2 (assign deferred conditions)
6127        // After phase 2, if there are more JOIN keywords, continue with another round
6128        loop {
6129            let joins_before = joins.len();
6130
6131            // Phase 1: Parse all JOINs with optional inline ON/USING conditions
6132            loop {
6133                let pos_before_join_kind = self.current;
6134                let join_kind_result = self.try_parse_join_kind();
6135                let (kind, needs_join_keyword, use_inner_keyword, use_outer_keyword, join_hint) =
6136                    match join_kind_result {
6137                        Some(r) => r,
6138                        None => break,
6139                    };
6140                // Collect comments from all tokens consumed by try_parse_join_kind:
6141                // - Leading comments on the first token (comments on a separate line before the join)
6142                // - Trailing comments between join keywords (e.g., INNER /* comment */ JOIN)
6143                let mut join_comments = Vec::new();
6144                // Capture leading comments from the first token of the join kind
6145                if pos_before_join_kind < self.tokens.len() {
6146                    join_comments
6147                        .extend(self.tokens[pos_before_join_kind].comments.iter().cloned());
6148                }
6149                for i in pos_before_join_kind..self.current {
6150                    if i < self.tokens.len() {
6151                        join_comments.extend(self.tokens[i].trailing_comments.iter().cloned());
6152                    }
6153                }
6154                // Snowflake: DIRECTED keyword before JOIN (e.g., CROSS DIRECTED JOIN)
6155                let directed = if needs_join_keyword && self.check_identifier("DIRECTED") {
6156                    self.skip();
6157                    true
6158                } else {
6159                    false
6160                };
6161                if needs_join_keyword {
6162                    self.expect(TokenType::Join)?;
6163                }
6164
6165                // ClickHouse: ARRAY JOIN uses expressions, not table references
6166                let table = if matches!(kind, JoinKind::Array | JoinKind::LeftArray) {
6167                    let mut items = Vec::new();
6168                    // Handle ARRAY JOIN with no arguments (intentional error test)
6169                    if !self.is_at_end()
6170                        && !self.check(TokenType::Semicolon)
6171                        && !self.check(TokenType::RParen)
6172                    {
6173                        loop {
6174                            let expr = self.parse_expression()?;
6175                            let item = if self.match_token(TokenType::As) {
6176                                let alias_name = self.expect_identifier_or_safe_keyword()?;
6177                                Expression::Alias(Box::new(Alias {
6178                                    this: expr,
6179                                    alias: Identifier::new(alias_name),
6180                                    column_aliases: Vec::new(),
6181                                    pre_alias_comments: Vec::new(),
6182                                    trailing_comments: Vec::new(),
6183                                    inferred_type: None,
6184                                }))
6185                            } else {
6186                                expr
6187                            };
6188                            items.push(item);
6189                            if !self.match_token(TokenType::Comma) {
6190                                break;
6191                            }
6192                        }
6193                    } // end if !is_at_end check
6194                    if items.len() == 1 {
6195                        items.pop().unwrap()
6196                    } else if items.is_empty() {
6197                        Expression::Null(Null)
6198                    } else {
6199                        Expression::Tuple(Box::new(Tuple { expressions: items }))
6200                    }
6201                } else {
6202                    self.parse_table_expression()?
6203                };
6204
6205                // Snowflake ASOF JOIN: OFFSET/LIMIT before MATCH_CONDITION are table aliases
6206                let table = if matches!(
6207                    kind,
6208                    JoinKind::AsOf | JoinKind::AsOfLeft | JoinKind::AsOfRight
6209                ) && (self.check(TokenType::Offset) || self.check(TokenType::Limit))
6210                    && self
6211                        .peek_nth(1)
6212                        .map(|t| t.text.eq_ignore_ascii_case("MATCH_CONDITION"))
6213                        == Some(true)
6214                {
6215                    let alias_name = self.advance().text.clone();
6216                    Expression::Alias(Box::new(Alias {
6217                        this: table,
6218                        alias: Identifier::new(alias_name),
6219                        column_aliases: Vec::new(),
6220                        pre_alias_comments: Vec::new(),
6221                        trailing_comments: Vec::new(),
6222                        inferred_type: None,
6223                    }))
6224                } else {
6225                    table
6226                };
6227
6228                // Try to parse inline MATCH_CONDITION/ON/USING (only if not followed by another JOIN)
6229                // We need to peek ahead to see if there's another JOIN keyword coming
6230                let has_match_condition = self.check_identifier("MATCH_CONDITION");
6231                let has_inline_condition = self.check(TokenType::On)
6232                    || self.check(TokenType::Using)
6233                    || has_match_condition;
6234                let next_is_join = self.check_join_keyword();
6235
6236                // Parse MATCH_CONDITION first (Snowflake ASOF JOIN can have MATCH_CONDITION before ON)
6237                let match_condition = if has_match_condition && !next_is_join {
6238                    if self.match_identifier("MATCH_CONDITION") {
6239                        self.expect(TokenType::LParen)?;
6240                        let condition = self.parse_expression()?;
6241                        self.expect(TokenType::RParen)?;
6242                        Some(condition)
6243                    } else {
6244                        None
6245                    }
6246                } else {
6247                    None
6248                };
6249
6250                let (on, using) = if (has_inline_condition || match_condition.is_some())
6251                    && !self.check_join_keyword()
6252                {
6253                    // Parse inline condition only if there's no more JOINs following
6254                    if self.match_token(TokenType::On) {
6255                        (Some(self.parse_expression()?), Vec::new())
6256                    } else if self.match_token(TokenType::Using) {
6257                        // ClickHouse allows USING without parentheses
6258                        let has_parens = self.match_token(TokenType::LParen);
6259                        // Use parse_using_column_list to handle qualified names like t1.col
6260                        let cols = self.parse_using_column_list()?;
6261                        if has_parens {
6262                            self.expect(TokenType::RParen)?;
6263                        }
6264                        (None, cols)
6265                    } else {
6266                        (None, Vec::new())
6267                    }
6268                } else {
6269                    (None, Vec::new())
6270                };
6271
6272                joins.push(Join {
6273                    this: table,
6274                    on,
6275                    using,
6276                    kind,
6277                    use_inner_keyword,
6278                    use_outer_keyword,
6279                    deferred_condition: false,
6280                    join_hint,
6281                    match_condition,
6282                    pivots: Vec::new(),
6283                    comments: join_comments,
6284                    nesting_group,
6285                    directed,
6286                });
6287            }
6288
6289            // Phase 2: Assign deferred ON/USING conditions to unconditioned joins (right-to-left)
6290            // Only consider joins from the current batch (joins_before..)
6291            let unconditioned: Vec<usize> = joins[joins_before..]
6292                .iter()
6293                .enumerate()
6294                .filter(|(_, j)| j.on.is_none() && j.using.is_empty())
6295                .map(|(i, _)| joins_before + i)
6296                .collect();
6297
6298            let mut idx = unconditioned.len();
6299            while idx > 0 {
6300                if self.match_token(TokenType::On) {
6301                    idx -= 1;
6302                    let join_idx = unconditioned[idx];
6303                    joins[join_idx].on = Some(self.parse_expression()?);
6304                    joins[join_idx].deferred_condition = true;
6305                } else if self.match_token(TokenType::Using) {
6306                    idx -= 1;
6307                    let join_idx = unconditioned[idx];
6308                    let has_parens = self.match_token(TokenType::LParen);
6309                    // Handle empty USING ()
6310                    let cols = if has_parens && self.check(TokenType::RParen) {
6311                        Vec::new()
6312                    } else {
6313                        // Use parse_using_column_list to handle qualified names like t1.col
6314                        self.parse_using_column_list()?
6315                    };
6316                    joins[join_idx].using = cols;
6317                    if has_parens {
6318                        self.expect(TokenType::RParen)?;
6319                    }
6320                    joins[join_idx].deferred_condition = true;
6321                } else {
6322                    break;
6323                }
6324            }
6325
6326            // If no new joins were parsed in this round, we're done
6327            if joins.len() == joins_before {
6328                break;
6329            }
6330
6331            // If there are more JOIN keywords after deferred conditions, continue with another round
6332            if !self.check_join_keyword() {
6333                break;
6334            }
6335            nesting_group += 1;
6336        }
6337
6338        Ok(joins)
6339    }
6340
6341    /// Check if the current token starts a JOIN clause
6342    fn check_join_keyword(&self) -> bool {
6343        self.check(TokenType::Join) ||
6344        self.check(TokenType::Inner) ||
6345        self.check(TokenType::Left) ||
6346        self.check(TokenType::Right) ||
6347        self.check(TokenType::Full) ||
6348        self.check(TokenType::Cross) ||
6349        self.check(TokenType::Natural) ||
6350        self.check(TokenType::Outer) ||
6351        // ClickHouse: ARRAY JOIN, GLOBAL JOIN, ALL JOIN, ANY JOIN, PASTE JOIN
6352        (matches!(self.config.dialect, Some(crate::dialects::DialectType::ClickHouse)) &&
6353            (self.check_identifier("ARRAY") || self.check_identifier("GLOBAL") || self.check(TokenType::All) || self.check(TokenType::Any) || self.check_identifier("PASTE")))
6354    }
6355
6356    /// Try to parse a JOIN kind
6357    /// Returns (JoinKind, needs_join_keyword, use_inner_keyword, use_outer_keyword, join_hint)
6358    fn try_parse_join_kind(&mut self) -> Option<(JoinKind, bool, bool, bool, Option<String>)> {
6359        if matches!(
6360            self.config.dialect,
6361            Some(crate::dialects::DialectType::ClickHouse)
6362        ) {
6363            let start = self.current;
6364            let mut global = false;
6365            let mut strictness: Option<String> = None;
6366            let mut kind: Option<JoinKind> = None;
6367            let mut use_outer = false;
6368            let mut use_inner = false;
6369
6370            if self.match_identifier("GLOBAL") {
6371                global = true;
6372            }
6373
6374            loop {
6375                if strictness.is_none() && self.match_token(TokenType::All) {
6376                    strictness = Some("ALL".to_string());
6377                    continue;
6378                }
6379                if strictness.is_none() && self.match_token(TokenType::Any) {
6380                    strictness = Some("ANY".to_string());
6381                    continue;
6382                }
6383                if strictness.is_none() && self.match_token(TokenType::AsOf) {
6384                    strictness = Some("ASOF".to_string());
6385                    continue;
6386                }
6387                if strictness.is_none() && self.match_token(TokenType::Semi) {
6388                    strictness = Some("SEMI".to_string());
6389                    continue;
6390                }
6391                if strictness.is_none() && self.match_token(TokenType::Anti) {
6392                    strictness = Some("ANTI".to_string());
6393                    continue;
6394                }
6395                if kind.is_none() && self.match_token(TokenType::Left) {
6396                    use_outer = self.match_token(TokenType::Outer);
6397                    use_inner = self.match_token(TokenType::Inner);
6398                    kind = Some(JoinKind::Left);
6399                    continue;
6400                }
6401                if kind.is_none() && self.match_token(TokenType::Right) {
6402                    use_outer = self.match_token(TokenType::Outer);
6403                    use_inner = self.match_token(TokenType::Inner);
6404                    kind = Some(JoinKind::Right);
6405                    continue;
6406                }
6407                if kind.is_none() && self.match_token(TokenType::Full) {
6408                    use_outer = self.match_token(TokenType::Outer);
6409                    kind = Some(JoinKind::Full);
6410                    continue;
6411                }
6412                if kind.is_none() && self.match_token(TokenType::Inner) {
6413                    use_inner = true;
6414                    kind = Some(JoinKind::Inner);
6415                    continue;
6416                }
6417                break;
6418            }
6419
6420            // ClickHouse: ARRAY JOIN or LEFT ARRAY JOIN
6421            if self.check_identifier("ARRAY") && self.check_next(TokenType::Join) {
6422                let array_kind = if matches!(kind, Some(JoinKind::Left)) {
6423                    JoinKind::LeftArray
6424                } else {
6425                    JoinKind::Array
6426                };
6427                self.skip(); // consume ARRAY
6428                                // JOIN will be consumed by caller
6429                return Some((array_kind, true, false, false, None));
6430            }
6431
6432            // ClickHouse: PASTE JOIN (positional join, no ON/USING)
6433            if self.check_identifier("PASTE") && self.check_next(TokenType::Join) {
6434                self.skip(); // consume PASTE
6435                                // JOIN will be consumed by caller
6436                return Some((JoinKind::Paste, true, false, false, None));
6437            }
6438
6439            if global || strictness.is_some() || kind.is_some() {
6440                if self.check(TokenType::Join) {
6441                    let join_kind = kind.unwrap_or(JoinKind::Inner);
6442                    let mut hints = Vec::new();
6443                    if global {
6444                        hints.push("GLOBAL".to_string());
6445                    }
6446                    if let Some(strict) = strictness {
6447                        hints.push(strict);
6448                    }
6449                    let join_hint = if hints.is_empty() {
6450                        None
6451                    } else {
6452                        Some(hints.join(" "))
6453                    };
6454                    return Some((join_kind, true, use_inner, use_outer, join_hint));
6455                } else {
6456                    self.current = start;
6457                }
6458            }
6459        }
6460
6461        // Check for ASOF first (DuckDB/Snowflake) - can be followed by LEFT/RIGHT/etc.
6462        if self.match_token(TokenType::AsOf) {
6463            // ASOF can be followed by LEFT, RIGHT, INNER, or standalone
6464            if self.match_token(TokenType::Left) {
6465                let use_outer = self.match_token(TokenType::Outer);
6466                Some((JoinKind::AsOfLeft, true, false, use_outer, None))
6467            } else if self.match_token(TokenType::Right) {
6468                let use_outer = self.match_token(TokenType::Outer);
6469                Some((JoinKind::AsOfRight, true, false, use_outer, None))
6470            } else if self.match_token(TokenType::Inner) {
6471                Some((JoinKind::AsOf, true, true, false, None))
6472            } else {
6473                // Standalone ASOF JOIN
6474                Some((JoinKind::AsOf, true, false, false, None))
6475            }
6476        } else if self.check(TokenType::Inner) {
6477            // Check if INNER is followed by a set operation (BigQuery INNER UNION/INTERSECT/EXCEPT)
6478            // In that case, don't treat it as a JOIN keyword
6479            let saved = self.current;
6480            self.skip(); // consume INNER
6481            if self.check(TokenType::Union)
6482                || self.check(TokenType::Intersect)
6483                || self.check(TokenType::Except)
6484            {
6485                self.current = saved; // backtrack
6486                return None;
6487            }
6488            // Check for TSQL join hints: INNER LOOP JOIN, INNER HASH JOIN, INNER MERGE JOIN
6489            let join_hint = self.parse_tsql_join_hint();
6490            Some((JoinKind::Inner, true, true, false, join_hint)) // INNER keyword was explicit
6491        } else if self.check(TokenType::Left) {
6492            // Check if LEFT is followed by a set operation (BigQuery LEFT UNION/INTERSECT/EXCEPT)
6493            let saved = self.current;
6494            self.skip(); // consume LEFT
6495                            // LEFT can be followed by OUTER/INNER then set op, or directly by set op
6496            let at_set_op = self.check(TokenType::Union)
6497                || self.check(TokenType::Intersect)
6498                || self.check(TokenType::Except);
6499            let at_inner_set_op = self.check(TokenType::Inner) && {
6500                let saved2 = self.current;
6501                self.skip();
6502                let is_setop = self.check(TokenType::Union)
6503                    || self.check(TokenType::Intersect)
6504                    || self.check(TokenType::Except);
6505                self.current = saved2;
6506                is_setop
6507            };
6508            if at_set_op || at_inner_set_op {
6509                self.current = saved; // backtrack
6510                return None;
6511            }
6512            // Continue with normal LEFT JOIN parsing
6513            self.current = saved;
6514            self.match_token(TokenType::Left); // re-consume LEFT
6515            let use_outer = self.match_token(TokenType::Outer);
6516            let use_inner = self.match_token(TokenType::Inner);
6517            let join_hint = self.parse_tsql_join_hint();
6518            // Check for SEMI, ANTI, or LATERAL
6519            if self.match_token(TokenType::Semi) {
6520                Some((JoinKind::LeftSemi, true, use_inner, use_outer, join_hint))
6521            } else if self.match_token(TokenType::Anti) {
6522                Some((JoinKind::LeftAnti, true, use_inner, use_outer, join_hint))
6523            } else if self.match_token(TokenType::Lateral) {
6524                Some((JoinKind::LeftLateral, true, use_inner, use_outer, join_hint))
6525            } else {
6526                Some((JoinKind::Left, true, use_inner, use_outer, join_hint))
6527            }
6528        } else if self.check(TokenType::Right) {
6529            // Check if RIGHT is followed by a set operation (BigQuery RIGHT UNION/INTERSECT/EXCEPT)
6530            let saved = self.current;
6531            self.skip(); // consume RIGHT
6532            let at_set_op = self.check(TokenType::Union)
6533                || self.check(TokenType::Intersect)
6534                || self.check(TokenType::Except);
6535            let at_inner_set_op = self.check(TokenType::Inner) && {
6536                let saved2 = self.current;
6537                self.skip();
6538                let is_setop = self.check(TokenType::Union)
6539                    || self.check(TokenType::Intersect)
6540                    || self.check(TokenType::Except);
6541                self.current = saved2;
6542                is_setop
6543            };
6544            if at_set_op || at_inner_set_op {
6545                self.current = saved; // backtrack
6546                return None;
6547            }
6548            // Continue with normal RIGHT JOIN parsing
6549            self.current = saved;
6550            self.match_token(TokenType::Right); // re-consume RIGHT
6551            let use_outer = self.match_token(TokenType::Outer);
6552            let use_inner = self.match_token(TokenType::Inner);
6553            let join_hint = self.parse_tsql_join_hint();
6554            // Check for SEMI or ANTI
6555            if self.match_token(TokenType::Semi) {
6556                Some((JoinKind::RightSemi, true, use_inner, use_outer, join_hint))
6557            } else if self.match_token(TokenType::Anti) {
6558                Some((JoinKind::RightAnti, true, use_inner, use_outer, join_hint))
6559            } else {
6560                Some((JoinKind::Right, true, use_inner, use_outer, join_hint))
6561            }
6562        } else if self.check(TokenType::Full) {
6563            // Check if FULL is followed by a set operation (BigQuery FULL UNION/INTERSECT/EXCEPT)
6564            let saved = self.current;
6565            self.skip(); // consume FULL
6566            let at_set_op = self.check(TokenType::Union)
6567                || self.check(TokenType::Intersect)
6568                || self.check(TokenType::Except);
6569            let at_inner_set_op = self.check(TokenType::Inner) && {
6570                let saved2 = self.current;
6571                self.skip();
6572                let is_setop = self.check(TokenType::Union)
6573                    || self.check(TokenType::Intersect)
6574                    || self.check(TokenType::Except);
6575                self.current = saved2;
6576                is_setop
6577            };
6578            if at_set_op || at_inner_set_op {
6579                self.current = saved; // backtrack
6580                return None;
6581            }
6582            // Continue with normal FULL JOIN parsing
6583            self.current = saved;
6584            self.match_token(TokenType::Full); // re-consume FULL
6585            let use_outer = self.match_token(TokenType::Outer);
6586            let join_hint = self.parse_tsql_join_hint();
6587            Some((JoinKind::Full, true, false, use_outer, join_hint))
6588        } else if self.match_token(TokenType::Cross) {
6589            // CROSS JOIN or CROSS APPLY
6590            if self.match_token(TokenType::Apply) {
6591                Some((JoinKind::CrossApply, false, false, false, None))
6592            } else {
6593                Some((JoinKind::Cross, true, false, false, None))
6594            }
6595        } else if self.match_token(TokenType::Natural) {
6596            // NATURAL can be followed by LEFT, RIGHT, INNER, FULL, or just JOIN
6597            if self.match_token(TokenType::Left) {
6598                let use_outer = self.match_token(TokenType::Outer);
6599                Some((JoinKind::NaturalLeft, true, false, use_outer, None))
6600            } else if self.match_token(TokenType::Right) {
6601                let use_outer = self.match_token(TokenType::Outer);
6602                Some((JoinKind::NaturalRight, true, false, use_outer, None))
6603            } else if self.match_token(TokenType::Full) {
6604                let use_outer = self.match_token(TokenType::Outer);
6605                Some((JoinKind::NaturalFull, true, false, use_outer, None))
6606            } else if self.match_token(TokenType::Inner) {
6607                Some((JoinKind::Natural, true, true, false, None))
6608            } else {
6609                Some((JoinKind::Natural, true, false, false, None))
6610            }
6611        } else if self.match_token(TokenType::Outer) {
6612            // OUTER APPLY or standalone OUTER JOIN
6613            if self.match_token(TokenType::Apply) {
6614                Some((JoinKind::OuterApply, false, false, true, None))
6615            } else {
6616                // Standalone OUTER JOIN (without LEFT/RIGHT/FULL)
6617                Some((JoinKind::Outer, true, false, true, None))
6618            }
6619        } else if self.check(TokenType::Lateral) {
6620            // Check if this is LATERAL VIEW (Hive/Spark syntax) vs LATERAL JOIN
6621            if self.current + 1 < self.tokens.len()
6622                && self.tokens[self.current + 1].token_type == TokenType::View
6623            {
6624                // LATERAL VIEW is not a JOIN type, return None
6625                None
6626            } else {
6627                self.skip(); // Consume LATERAL
6628                Some((JoinKind::Lateral, true, false, false, None))
6629            }
6630        } else if self.match_token(TokenType::Semi) {
6631            Some((JoinKind::Semi, true, false, false, None))
6632        } else if self.match_token(TokenType::Anti) {
6633            Some((JoinKind::Anti, true, false, false, None))
6634        } else if self.check_identifier("POSITIONAL") && self.check_next(TokenType::Join) {
6635            // DuckDB POSITIONAL JOIN
6636            self.skip(); // consume POSITIONAL
6637            Some((JoinKind::Positional, true, false, false, None))
6638        } else if self.match_token(TokenType::StraightJoin) {
6639            // STRAIGHT_JOIN in MySQL - doesn't need JOIN keyword after it
6640            Some((JoinKind::Straight, false, false, false, None))
6641        } else if self.check(TokenType::Join) {
6642            Some((JoinKind::Inner, true, false, false, None)) // Default JOIN is INNER (without explicit INNER keyword)
6643        } else if self.match_token(TokenType::Comma) {
6644            // Comma-separated tables: FROM a, b (old-style ANSI join syntax)
6645            Some((JoinKind::Implicit, false, false, false, None)) // No JOIN keyword needed
6646        } else {
6647            None
6648        }
6649    }
6650
6651    /// Parse TSQL join hints: LOOP, HASH, MERGE, REMOTE
6652    fn parse_tsql_join_hint(&mut self) -> Option<String> {
6653        if self.check_identifier("LOOP") {
6654            self.skip();
6655            Some("LOOP".to_string())
6656        } else if self.check_identifier("HASH") {
6657            self.skip();
6658            Some("HASH".to_string())
6659        } else if self.check_identifier("REMOTE") {
6660            self.skip();
6661            Some("REMOTE".to_string())
6662        } else if self.check(TokenType::Merge) && {
6663            // Be careful: MERGE is also a keyword for MERGE statement
6664            // Only treat as hint if followed by JOIN
6665            let next_pos = self.current + 1;
6666            next_pos < self.tokens.len() && self.tokens[next_pos].token_type == TokenType::Join
6667        } {
6668            self.skip();
6669            Some("MERGE".to_string())
6670        } else {
6671            None
6672        }
6673    }
6674
6675    /// Parse GROUP BY clause
6676    fn parse_group_by(&mut self) -> Result<GroupBy> {
6677        // Check for optional ALL/DISTINCT modifier
6678        // Some(true) = ALL, Some(false) = DISTINCT, None = no modifier
6679        let all = if self.match_token(TokenType::All) {
6680            Some(true)
6681        } else if self.match_token(TokenType::Distinct) {
6682            Some(false)
6683        } else {
6684            None
6685        };
6686
6687        let mut expressions = Vec::new();
6688
6689        // GROUP BY ALL / GROUP BY DISTINCT without following CUBE/ROLLUP/expressions
6690        // should return early (e.g., Snowflake's "GROUP BY ALL" without column list).
6691        // But in Presto/Trino, ALL/DISTINCT can be followed by CUBE/ROLLUP expressions.
6692        if all.is_some() && self.is_at_query_modifier_or_end() {
6693            return Ok(GroupBy {
6694                expressions,
6695                all,
6696                totals: false,
6697                comments: Vec::new(),
6698            });
6699        }
6700
6701        // GROUP BY ALL WITH ROLLUP/CUBE/TOTALS — skip expression parsing, go straight to modifiers
6702        if all.is_some()
6703            && self.check(TokenType::With)
6704            && (self.check_next(TokenType::Cube)
6705                || self.check_next(TokenType::Rollup)
6706                || self.check_next_identifier("TOTALS"))
6707        {
6708            let mut totals = false;
6709            // Process WITH ROLLUP/CUBE
6710            if self.check_next(TokenType::Cube) || self.check_next(TokenType::Rollup) {
6711                self.skip(); // consume WITH
6712                if self.match_token(TokenType::Cube) {
6713                    expressions.push(Expression::Cube(Box::new(Cube {
6714                        expressions: Vec::new(),
6715                    })));
6716                } else if self.match_token(TokenType::Rollup) {
6717                    expressions.push(Expression::Rollup(Box::new(Rollup {
6718                        expressions: Vec::new(),
6719                    })));
6720                }
6721            }
6722            // Check for WITH TOTALS (possibly chained after ROLLUP/CUBE)
6723            if self.check(TokenType::With) && self.check_next_identifier("TOTALS") {
6724                self.skip(); // WITH
6725                self.skip(); // TOTALS
6726                totals = true;
6727            }
6728            return Ok(GroupBy {
6729                expressions,
6730                all,
6731                totals,
6732                comments: Vec::new(),
6733            });
6734        }
6735
6736        loop {
6737            // Check for GROUPING SETS, CUBE, ROLLUP
6738            let expr = if self.check_identifier("GROUPING")
6739                && self
6740                    .peek_nth(1)
6741                    .map_or(false, |t| t.text.eq_ignore_ascii_case("SETS"))
6742                && {
6743                    self.skip();
6744                    self.skip();
6745                    true
6746                } {
6747                // GROUPING SETS (...)
6748                self.expect(TokenType::LParen)?;
6749                let args = self.parse_grouping_sets_args()?;
6750                self.expect(TokenType::RParen)?;
6751                Expression::Function(Box::new(Function {
6752                    name: "GROUPING SETS".to_string(),
6753                    args,
6754                    distinct: false,
6755                    trailing_comments: Vec::new(),
6756                    use_bracket_syntax: false,
6757                    no_parens: false,
6758                    quoted: false,
6759                    span: None,
6760                    inferred_type: None,
6761                }))
6762            } else if self.match_token(TokenType::Cube) {
6763                // CUBE (...)
6764                self.expect(TokenType::LParen)?;
6765                let args = self.parse_expression_list()?;
6766                self.expect(TokenType::RParen)?;
6767                Expression::Function(Box::new(Function {
6768                    name: "CUBE".to_string(),
6769                    args,
6770                    distinct: false,
6771                    trailing_comments: Vec::new(),
6772                    use_bracket_syntax: false,
6773                    no_parens: false,
6774                    quoted: false,
6775                    span: None,
6776                    inferred_type: None,
6777                }))
6778            } else if self.match_token(TokenType::Rollup) {
6779                // ROLLUP (...)
6780                self.expect(TokenType::LParen)?;
6781                let args = self.parse_expression_list()?;
6782                self.expect(TokenType::RParen)?;
6783                Expression::Function(Box::new(Function {
6784                    name: "ROLLUP".to_string(),
6785                    args,
6786                    distinct: false,
6787                    trailing_comments: Vec::new(),
6788                    use_bracket_syntax: false,
6789                    no_parens: false,
6790                    quoted: false,
6791                    span: None,
6792                    inferred_type: None,
6793                }))
6794            } else {
6795                self.parse_expression()?
6796            };
6797
6798            // ClickHouse: GROUP BY expr AS alias
6799            let expr = if matches!(
6800                self.config.dialect,
6801                Some(crate::dialects::DialectType::ClickHouse)
6802            ) && self.check(TokenType::As)
6803                && !self.check_next(TokenType::LParen)
6804            {
6805                self.skip(); // consume AS
6806                let alias = self.expect_identifier_or_keyword_with_quoted()?;
6807                Expression::Alias(Box::new(Alias::new(expr, alias)))
6808            } else {
6809                expr
6810            };
6811
6812            expressions.push(expr);
6813
6814            if !self.match_token(TokenType::Comma) {
6815                // Allow adjacent CUBE/ROLLUP/GROUPING SETS without comma separator
6816                // e.g., GROUP BY CUBE(a) ROLLUP(b), GROUPING SETS((c, d))
6817                if self.check(TokenType::Cube)
6818                    || self.check(TokenType::Rollup)
6819                    || (self.check_identifier("GROUPING")
6820                        && self
6821                            .peek_nth(1)
6822                            .map_or(false, |t| t.text.eq_ignore_ascii_case("SETS")))
6823                {
6824                    continue;
6825                }
6826                break;
6827            }
6828        }
6829
6830        // Check for trailing WITH CUBE or WITH ROLLUP (Hive/MySQL syntax)
6831        // This is different from CUBE(...) or ROLLUP(...) which are parsed inline above
6832        // Use lookahead to avoid consuming WITH if it's not followed by CUBE or ROLLUP
6833        // (e.g., Redshift's WITH NO SCHEMA BINDING should not be consumed here)
6834        if self.check(TokenType::With)
6835            && (self.check_next(TokenType::Cube) || self.check_next(TokenType::Rollup))
6836        {
6837            self.skip(); // consume WITH
6838            if self.match_token(TokenType::Cube) {
6839                // WITH CUBE - add Cube with empty expressions
6840                expressions.push(Expression::Cube(Box::new(Cube {
6841                    expressions: Vec::new(),
6842                })));
6843            } else if self.match_token(TokenType::Rollup) {
6844                // WITH ROLLUP - add Rollup with empty expressions
6845                expressions.push(Expression::Rollup(Box::new(Rollup {
6846                    expressions: Vec::new(),
6847                })));
6848            }
6849        }
6850
6851        // ClickHouse: WITH TOTALS
6852        let totals = if self.check(TokenType::With) && self.check_next_identifier("TOTALS") {
6853            self.skip(); // consume WITH
6854            self.skip(); // consume TOTALS
6855            true
6856        } else {
6857            false
6858        };
6859
6860        Ok(GroupBy {
6861            expressions,
6862            all,
6863            totals,
6864            comments: Vec::new(),
6865        })
6866    }
6867
6868    /// Parse GROUPING SETS arguments which can include tuples like (x, y), nested GROUPING SETS, CUBE, ROLLUP
6869    fn parse_grouping_sets_args(&mut self) -> Result<Vec<Expression>> {
6870        let mut args = Vec::new();
6871
6872        loop {
6873            // Check for nested GROUPING SETS, CUBE, ROLLUP
6874            let expr = if self.check_identifier("GROUPING")
6875                && self
6876                    .peek_nth(1)
6877                    .map_or(false, |t| t.text.eq_ignore_ascii_case("SETS"))
6878                && {
6879                    self.skip();
6880                    self.skip();
6881                    true
6882                } {
6883                // Nested GROUPING SETS (...)
6884                self.expect(TokenType::LParen)?;
6885                let inner_args = self.parse_grouping_sets_args()?;
6886                self.expect(TokenType::RParen)?;
6887                Expression::Function(Box::new(Function {
6888                    name: "GROUPING SETS".to_string(),
6889                    args: inner_args,
6890                    distinct: false,
6891                    trailing_comments: Vec::new(),
6892                    use_bracket_syntax: false,
6893                    no_parens: false,
6894                    quoted: false,
6895                    span: None,
6896                    inferred_type: None,
6897                }))
6898            } else if self.match_token(TokenType::Cube) {
6899                // CUBE (...)
6900                self.expect(TokenType::LParen)?;
6901                let inner_args = self.parse_expression_list()?;
6902                self.expect(TokenType::RParen)?;
6903                Expression::Function(Box::new(Function {
6904                    name: "CUBE".to_string(),
6905                    args: inner_args,
6906                    distinct: false,
6907                    trailing_comments: Vec::new(),
6908                    use_bracket_syntax: false,
6909                    no_parens: false,
6910                    quoted: false,
6911                    span: None,
6912                    inferred_type: None,
6913                }))
6914            } else if self.match_token(TokenType::Rollup) {
6915                // ROLLUP (...)
6916                self.expect(TokenType::LParen)?;
6917                let inner_args = self.parse_expression_list()?;
6918                self.expect(TokenType::RParen)?;
6919                Expression::Function(Box::new(Function {
6920                    name: "ROLLUP".to_string(),
6921                    args: inner_args,
6922                    distinct: false,
6923                    trailing_comments: Vec::new(),
6924                    use_bracket_syntax: false,
6925                    no_parens: false,
6926                    quoted: false,
6927                    span: None,
6928                    inferred_type: None,
6929                }))
6930            } else if self.check(TokenType::LParen) {
6931                // This could be a tuple like (x, y) or empty ()
6932                self.skip(); // consume (
6933                if self.check(TokenType::RParen) {
6934                    // Empty tuple ()
6935                    self.skip();
6936                    Expression::Tuple(Box::new(Tuple {
6937                        expressions: Vec::new(),
6938                    }))
6939                } else {
6940                    let inner = self.parse_expression_list()?;
6941                    self.expect(TokenType::RParen)?;
6942                    Expression::Tuple(Box::new(Tuple { expressions: inner }))
6943                }
6944            } else {
6945                self.parse_expression()?
6946            };
6947
6948            args.push(expr);
6949
6950            if !self.match_token(TokenType::Comma) {
6951                break;
6952            }
6953        }
6954
6955        Ok(args)
6956    }
6957
6958    /// Parse ORDER BY clause
6959    fn parse_order_by(&mut self) -> Result<OrderBy> {
6960        self.parse_order_by_with_siblings(false)
6961    }
6962
6963    /// Parse ORDER BY clause with optional siblings flag (Oracle ORDER SIBLINGS BY)
6964    fn parse_order_by_with_siblings(&mut self, siblings: bool) -> Result<OrderBy> {
6965        let mut expressions = Vec::new();
6966
6967        loop {
6968            let expr = self.parse_expression()?;
6969
6970            // ClickHouse: ORDER BY expr AS alias — allow AS alias before DESC/ASC
6971            // But NOT AS SELECT/WITH which would be CREATE TABLE ... AS SELECT
6972            let expr = if matches!(
6973                self.config.dialect,
6974                Some(crate::dialects::DialectType::ClickHouse)
6975            ) && self.check(TokenType::As)
6976                && !self.check_next(TokenType::LParen)
6977                && !self.check_next(TokenType::Select)
6978                && !self.check_next(TokenType::With)
6979            {
6980                self.skip(); // consume AS
6981                let alias = self.expect_identifier_or_keyword_with_quoted()?;
6982                Expression::Alias(Box::new(Alias::new(expr, alias)))
6983            } else {
6984                expr
6985            };
6986
6987            let (desc, explicit_asc) = if self.match_token(TokenType::Desc) {
6988                (true, false)
6989            } else if self.match_token(TokenType::Asc) {
6990                (false, true)
6991            } else {
6992                (false, false)
6993            };
6994
6995            let nulls_first = if self.match_token(TokenType::Nulls) {
6996                if self.match_token(TokenType::First) {
6997                    Some(true)
6998                } else if self.match_token(TokenType::Last) {
6999                    Some(false)
7000                } else {
7001                    return Err(self.parse_error("Expected FIRST or LAST after NULLS"));
7002                }
7003            } else {
7004                None
7005            };
7006
7007            // Parse optional WITH FILL clause (ClickHouse)
7008            let with_fill = if self.match_text_seq(&["WITH", "FILL"]) {
7009                let from_ = if self.match_token(TokenType::From) {
7010                    Some(Box::new(self.parse_or()?))
7011                } else {
7012                    None
7013                };
7014                let to = if self.match_text_seq(&["TO"]) {
7015                    Some(Box::new(self.parse_or()?))
7016                } else {
7017                    None
7018                };
7019                let step = if self.match_text_seq(&["STEP"]) {
7020                    Some(Box::new(self.parse_or()?))
7021                } else {
7022                    None
7023                };
7024                // ClickHouse: STALENESS [INTERVAL] expr
7025                let staleness = if self.match_text_seq(&["STALENESS"]) {
7026                    Some(Box::new(self.parse_or()?))
7027                } else {
7028                    None
7029                };
7030                let interpolate = if self.match_text_seq(&["INTERPOLATE"]) {
7031                    if self.match_token(TokenType::LParen) {
7032                        // Parse INTERPOLATE items: identifier [AS expression], ...
7033                        let mut items = Vec::new();
7034                        loop {
7035                            if self.check(TokenType::RParen) {
7036                                break;
7037                            }
7038                            let quoted = self.check(TokenType::QuotedIdentifier);
7039                            let name_text = self.expect_identifier_or_safe_keyword()?;
7040                            let name_id = Identifier {
7041                                name: name_text,
7042                                quoted,
7043                                trailing_comments: Vec::new(),
7044                                span: None,
7045                            };
7046                            let item = if self.match_token(TokenType::As) {
7047                                let expr = self.parse_expression()?;
7048                                // Store as Alias: this=expression, alias=name
7049                                Expression::Alias(Box::new(Alias {
7050                                    this: expr,
7051                                    alias: name_id,
7052                                    column_aliases: Vec::new(),
7053                                    pre_alias_comments: Vec::new(),
7054                                    trailing_comments: Vec::new(),
7055                                    inferred_type: None,
7056                                }))
7057                            } else {
7058                                Expression::Identifier(name_id)
7059                            };
7060                            items.push(item);
7061                            if !self.match_token(TokenType::Comma) {
7062                                break;
7063                            }
7064                        }
7065                        self.expect(TokenType::RParen)?;
7066                        if items.len() == 1 {
7067                            Some(Box::new(items.into_iter().next().unwrap()))
7068                        } else {
7069                            Some(Box::new(Expression::Tuple(Box::new(
7070                                crate::expressions::Tuple { expressions: items },
7071                            ))))
7072                        }
7073                    } else {
7074                        None
7075                    }
7076                } else {
7077                    None
7078                };
7079                Some(Box::new(WithFill {
7080                    from_,
7081                    to,
7082                    step,
7083                    staleness,
7084                    interpolate,
7085                }))
7086            } else {
7087                None
7088            };
7089
7090            expressions.push(Ordered {
7091                this: expr,
7092                desc,
7093                nulls_first,
7094                explicit_asc,
7095                with_fill,
7096            });
7097
7098            if !self.match_token(TokenType::Comma) {
7099                break;
7100            }
7101
7102            // Handle trailing comma: if at end of input or semicolon, break
7103            if self.is_at_end() || self.check(TokenType::Semicolon) {
7104                break;
7105            }
7106        }
7107
7108        Ok(OrderBy {
7109            expressions,
7110            siblings,
7111            comments: Vec::new(),
7112        })
7113    }
7114
7115    /// Parse query modifiers (ORDER BY, LIMIT, OFFSET, DISTRIBUTE BY, SORT BY, CLUSTER BY) for parenthesized queries
7116    /// e.g., (SELECT 1) ORDER BY x LIMIT 1 OFFSET 1
7117    /// e.g., (SELECT 1 UNION SELECT 2) DISTRIBUTE BY z SORT BY x
7118    fn parse_query_modifiers(&mut self, inner: Expression) -> Result<Expression> {
7119        // Parse DISTRIBUTE BY (Hive/Spark)
7120        let distribute_by = if self.match_keywords(&[TokenType::Distribute, TokenType::By]) {
7121            let exprs = self.parse_expression_list()?;
7122            Some(DistributeBy { expressions: exprs })
7123        } else {
7124            None
7125        };
7126
7127        // Parse SORT BY (Hive/Spark) or CLUSTER BY (Hive/Spark)
7128        let (sort_by, cluster_by) = if self.match_keywords(&[TokenType::Sort, TokenType::By]) {
7129            // SORT BY
7130            let mut orders = Vec::new();
7131            loop {
7132                if let Some(ordered) = self.parse_ordered_item()? {
7133                    orders.push(ordered);
7134                } else {
7135                    break;
7136                }
7137                if !self.match_token(TokenType::Comma) {
7138                    break;
7139                }
7140            }
7141            (
7142                Some(SortBy {
7143                    expressions: orders,
7144                }),
7145                None,
7146            )
7147        } else if self.match_keywords(&[TokenType::Cluster, TokenType::By]) {
7148            // CLUSTER BY
7149            let mut orders = Vec::new();
7150            loop {
7151                if let Some(ordered) = self.parse_ordered_item()? {
7152                    orders.push(ordered);
7153                } else {
7154                    break;
7155                }
7156                if !self.match_token(TokenType::Comma) {
7157                    break;
7158                }
7159            }
7160            (
7161                None,
7162                Some(ClusterBy {
7163                    expressions: orders,
7164                }),
7165            )
7166        } else {
7167            (None, None)
7168        };
7169
7170        // Parse ORDER BY
7171        let order_by = if self.match_keywords(&[TokenType::Order, TokenType::By]) {
7172            Some(self.parse_order_by()?)
7173        } else {
7174            None
7175        };
7176
7177        // Parse LIMIT
7178        let limit = if self.match_token(TokenType::Limit) {
7179            Some(Limit {
7180                this: self.parse_expression()?,
7181                percent: false,
7182                comments: Vec::new(),
7183            })
7184        } else {
7185            None
7186        };
7187
7188        // Parse OFFSET
7189        let offset = if self.match_token(TokenType::Offset) {
7190            Some(Offset {
7191                this: self.parse_expression()?,
7192                rows: None,
7193            })
7194        } else {
7195            None
7196        };
7197
7198        // If we have any modifiers, wrap in a Subquery with the modifiers
7199        if order_by.is_some()
7200            || limit.is_some()
7201            || offset.is_some()
7202            || distribute_by.is_some()
7203            || sort_by.is_some()
7204            || cluster_by.is_some()
7205        {
7206            // If inner is already a Subquery, add modifiers to it instead of double-wrapping
7207            if let Expression::Subquery(mut subq) = inner {
7208                subq.order_by = order_by;
7209                subq.limit = limit;
7210                subq.offset = offset;
7211                subq.distribute_by = distribute_by;
7212                subq.sort_by = sort_by;
7213                subq.cluster_by = cluster_by;
7214                Ok(Expression::Subquery(subq))
7215            } else if let Expression::Paren(paren) = inner {
7216                // If inner is a Paren containing a Subquery or other query, unwrap it
7217                // and add modifiers to a new Subquery wrapping the Paren
7218                // This handles cases like ((SELECT 1)) LIMIT 1
7219                Ok(Expression::Subquery(Box::new(Subquery {
7220                    this: Expression::Paren(paren),
7221                    alias: None,
7222                    column_aliases: Vec::new(),
7223                    order_by,
7224                    limit,
7225                    offset,
7226                    distribute_by,
7227                    sort_by,
7228                    cluster_by,
7229                    lateral: false,
7230                    modifiers_inside: false,
7231                    trailing_comments: Vec::new(),
7232                    inferred_type: None,
7233                })))
7234            } else {
7235                Ok(Expression::Subquery(Box::new(Subquery {
7236                    this: inner,
7237                    alias: None,
7238                    column_aliases: Vec::new(),
7239                    order_by,
7240                    limit,
7241                    offset,
7242                    distribute_by,
7243                    sort_by,
7244                    cluster_by,
7245                    lateral: false,
7246                    modifiers_inside: false,
7247                    trailing_comments: Vec::new(),
7248                    inferred_type: None,
7249                })))
7250            }
7251        } else {
7252            // No modifiers - return inner as-is (don't double-wrap if already a Subquery)
7253            Ok(inner)
7254        }
7255    }
7256
7257    /// Parse ORDER BY expressions for use inside aggregate functions
7258    /// Returns Vec<Ordered> instead of OrderBy struct
7259    fn parse_order_by_list(&mut self) -> Result<Vec<Ordered>> {
7260        let mut expressions = Vec::new();
7261
7262        loop {
7263            let expr = self.parse_expression()?;
7264
7265            let (desc, explicit_asc) = if self.match_token(TokenType::Desc) {
7266                (true, false)
7267            } else if self.match_token(TokenType::Asc) {
7268                (false, true)
7269            } else {
7270                (false, false)
7271            };
7272
7273            let nulls_first = if self.match_token(TokenType::Nulls) {
7274                if self.match_token(TokenType::First) {
7275                    Some(true)
7276                } else if self.match_token(TokenType::Last) {
7277                    Some(false)
7278                } else {
7279                    return Err(self.parse_error("Expected FIRST or LAST after NULLS"));
7280                }
7281            } else {
7282                None
7283            };
7284
7285            expressions.push(Ordered {
7286                this: expr,
7287                desc,
7288                nulls_first,
7289                explicit_asc,
7290                with_fill: None,
7291            });
7292
7293            if !self.match_token(TokenType::Comma) {
7294                break;
7295            }
7296        }
7297
7298        Ok(expressions)
7299    }
7300
7301    /// Parse DISTRIBUTE BY clause (Hive/Spark)
7302    fn parse_distribute_by(&mut self) -> Result<DistributeBy> {
7303        let mut expressions = Vec::new();
7304
7305        loop {
7306            expressions.push(self.parse_expression()?);
7307            if !self.match_token(TokenType::Comma) {
7308                break;
7309            }
7310        }
7311
7312        Ok(DistributeBy { expressions })
7313    }
7314
7315    /// Parse CLUSTER BY clause (Hive/Spark)
7316    fn parse_cluster_by(&mut self) -> Result<ClusterBy> {
7317        let mut expressions = Vec::new();
7318
7319        loop {
7320            let expr = self.parse_expression()?;
7321
7322            let (desc, explicit_asc) = if self.match_token(TokenType::Desc) {
7323                (true, false)
7324            } else if self.match_token(TokenType::Asc) {
7325                (false, true)
7326            } else {
7327                (false, false)
7328            };
7329
7330            expressions.push(Ordered {
7331                this: expr,
7332                desc,
7333                nulls_first: None,
7334                explicit_asc,
7335                with_fill: None,
7336            });
7337
7338            if !self.match_token(TokenType::Comma) {
7339                break;
7340            }
7341        }
7342
7343        Ok(ClusterBy { expressions })
7344    }
7345
7346    /// Parse SORT BY clause (Hive/Spark)
7347    fn parse_sort_by(&mut self) -> Result<SortBy> {
7348        let mut expressions = Vec::new();
7349
7350        loop {
7351            let expr = self.parse_expression()?;
7352
7353            let (desc, explicit_asc) = if self.match_token(TokenType::Desc) {
7354                (true, false)
7355            } else if self.match_token(TokenType::Asc) {
7356                (false, true)
7357            } else {
7358                (false, false)
7359            };
7360
7361            let nulls_first = if self.match_token(TokenType::Nulls) {
7362                if self.match_token(TokenType::First) {
7363                    Some(true)
7364                } else if self.match_token(TokenType::Last) {
7365                    Some(false)
7366                } else {
7367                    return Err(self.parse_error("Expected FIRST or LAST after NULLS"));
7368                }
7369            } else {
7370                None
7371            };
7372
7373            expressions.push(Ordered {
7374                this: expr,
7375                desc,
7376                nulls_first,
7377                explicit_asc,
7378                with_fill: None,
7379            });
7380
7381            if !self.match_token(TokenType::Comma) {
7382                break;
7383            }
7384        }
7385
7386        Ok(SortBy { expressions })
7387    }
7388
7389    /// Parse FOR UPDATE/SHARE locking clauses or FOR XML (T-SQL)
7390    /// Syntax: FOR UPDATE|SHARE|NO KEY UPDATE|KEY SHARE [OF tables] [NOWAIT|WAIT n|SKIP LOCKED]
7391    /// Also handles: LOCK IN SHARE MODE (MySQL)
7392    /// Also handles: FOR XML PATH|RAW|AUTO|EXPLICIT [, options...] (T-SQL)
7393    fn parse_locks_and_for_xml(&mut self) -> Result<(Vec<Lock>, Vec<Expression>)> {
7394        let mut locks = Vec::new();
7395        let mut for_xml = Vec::new();
7396
7397        loop {
7398            let (update, key) = if self.match_keywords(&[TokenType::For, TokenType::Update]) {
7399                // FOR UPDATE
7400                (
7401                    Some(Box::new(Expression::Boolean(BooleanLiteral {
7402                        value: true,
7403                    }))),
7404                    None,
7405                )
7406            } else if self.check(TokenType::For) && self.check_next_identifier("XML") {
7407                // FOR XML (T-SQL) - parse XML options
7408                self.skip(); // consume FOR
7409                self.skip(); // consume XML
7410                for_xml = self.parse_for_xml_options()?;
7411                break; // FOR XML is always the last clause
7412            } else if self.check(TokenType::For) && self.check_next_identifier("SHARE") {
7413                // FOR SHARE
7414                self.skip(); // consume FOR
7415                self.skip(); // consume SHARE
7416                (None, None)
7417            } else if self.check_identifier("LOCK") && self.check_next(TokenType::In) {
7418                // LOCK IN SHARE MODE (MySQL) -> converted to FOR SHARE
7419                self.skip(); // consume LOCK
7420                self.skip(); // consume IN
7421                if self.match_identifier("SHARE") {
7422                    let _ = self.match_identifier("MODE");
7423                }
7424                (None, None)
7425            } else if self.check(TokenType::For) && self.check_next(TokenType::Key) {
7426                // FOR KEY SHARE (PostgreSQL)
7427                self.skip(); // consume FOR
7428                self.skip(); // consume KEY
7429                if !self.match_identifier("SHARE") {
7430                    break; // Not a valid lock clause
7431                }
7432                (
7433                    None,
7434                    Some(Box::new(Expression::Boolean(BooleanLiteral {
7435                        value: true,
7436                    }))),
7437                )
7438            } else if self.check(TokenType::For) && self.check_next(TokenType::No) {
7439                // FOR NO KEY UPDATE (PostgreSQL)
7440                self.skip(); // consume FOR
7441                self.skip(); // consume NO
7442                if !self.match_identifier("KEY") || !self.match_token(TokenType::Update) {
7443                    break; // Not a valid lock clause
7444                }
7445                (
7446                    Some(Box::new(Expression::Boolean(BooleanLiteral {
7447                        value: true,
7448                    }))),
7449                    Some(Box::new(Expression::Boolean(BooleanLiteral {
7450                        value: true,
7451                    }))),
7452                )
7453            } else {
7454                // No more lock clauses
7455                break;
7456            };
7457
7458            // Parse optional OF clause: OF table1, table2
7459            let expressions = if self.match_token(TokenType::Of) {
7460                let mut tables = Vec::new();
7461                loop {
7462                    // Parse table reference (can be schema.table or just table)
7463                    let table = self.parse_table_ref()?;
7464                    tables.push(Expression::Table(Box::new(table)));
7465                    if !self.match_token(TokenType::Comma) {
7466                        break;
7467                    }
7468                }
7469                tables
7470            } else {
7471                Vec::new()
7472            };
7473
7474            // Parse wait option: NOWAIT, WAIT n, or SKIP LOCKED
7475            // Following Python sqlglot convention:
7476            // - NOWAIT -> Boolean(true)
7477            // - SKIP LOCKED -> Boolean(false)
7478            // - WAIT n -> Literal (the number)
7479            let wait = if self.match_identifier("NOWAIT") {
7480                // NOWAIT -> represented as Boolean(true)
7481                Some(Box::new(Expression::Boolean(BooleanLiteral {
7482                    value: true,
7483                })))
7484            } else if self.match_identifier("WAIT") {
7485                // WAIT n -> wait = expression (the number/literal)
7486                Some(Box::new(self.parse_primary()?))
7487            } else if self.match_identifier("SKIP") && self.match_identifier("LOCKED") {
7488                // SKIP LOCKED -> represented as Boolean(false)
7489                Some(Box::new(Expression::Boolean(BooleanLiteral {
7490                    value: false,
7491                })))
7492            } else {
7493                None
7494            };
7495
7496            locks.push(Lock {
7497                update,
7498                expressions,
7499                wait,
7500                key,
7501            });
7502        }
7503
7504        Ok((locks, for_xml))
7505    }
7506
7507    /// Parse FOR XML options (T-SQL)
7508    /// Syntax: FOR XML PATH|RAW|AUTO|EXPLICIT [('element')] [, BINARY BASE64] [, ELEMENTS [XSINIL|ABSENT]] [, TYPE] [, ROOT('name')]
7509    fn parse_for_xml_options(&mut self) -> Result<Vec<Expression>> {
7510        let mut options = Vec::new();
7511
7512        loop {
7513            // Parse XML option: could be a known option (PATH, RAW, AUTO, EXPLICIT, BINARY, ELEMENTS, TYPE, ROOT)
7514            // or an XMLKeyValueOption like PATH('element')
7515            if let Some(opt) = self.parse_for_xml_single_option()? {
7516                options.push(opt);
7517            } else {
7518                break;
7519            }
7520
7521            // Check for comma to continue parsing more options
7522            if !self.match_token(TokenType::Comma) {
7523                break;
7524            }
7525        }
7526
7527        Ok(options)
7528    }
7529
7530    /// Parse a single FOR XML option
7531    fn parse_for_xml_single_option(&mut self) -> Result<Option<Expression>> {
7532        // Known XML modes: PATH, RAW, AUTO, EXPLICIT
7533        // Known options: BINARY BASE64, ELEMENTS [XSINIL|ABSENT], TYPE, ROOT('name')
7534
7535        // Try to match known patterns
7536        if self.match_identifier("PATH") {
7537            let expression = if self.match_token(TokenType::LParen) {
7538                let expr = self.parse_string()?;
7539                self.expect(TokenType::RParen)?;
7540                expr
7541            } else {
7542                None
7543            };
7544            return Ok(Some(Expression::QueryOption(Box::new(QueryOption {
7545                this: Box::new(Expression::Var(Box::new(Var {
7546                    this: "PATH".to_string(),
7547                }))),
7548                expression: expression.map(|e| Box::new(e)),
7549            }))));
7550        }
7551
7552        if self.match_identifier("RAW") {
7553            let expression = if self.match_token(TokenType::LParen) {
7554                let expr = self.parse_string()?;
7555                self.expect(TokenType::RParen)?;
7556                expr
7557            } else {
7558                None
7559            };
7560            return Ok(Some(Expression::QueryOption(Box::new(QueryOption {
7561                this: Box::new(Expression::Var(Box::new(Var {
7562                    this: "RAW".to_string(),
7563                }))),
7564                expression: expression.map(|e| Box::new(e)),
7565            }))));
7566        }
7567
7568        if self.match_identifier("AUTO") {
7569            return Ok(Some(Expression::QueryOption(Box::new(QueryOption {
7570                this: Box::new(Expression::Var(Box::new(Var {
7571                    this: "AUTO".to_string(),
7572                }))),
7573                expression: None,
7574            }))));
7575        }
7576
7577        if self.match_identifier("EXPLICIT") {
7578            return Ok(Some(Expression::QueryOption(Box::new(QueryOption {
7579                this: Box::new(Expression::Var(Box::new(Var {
7580                    this: "EXPLICIT".to_string(),
7581                }))),
7582                expression: None,
7583            }))));
7584        }
7585
7586        if self.match_identifier("TYPE") {
7587            return Ok(Some(Expression::QueryOption(Box::new(QueryOption {
7588                this: Box::new(Expression::Var(Box::new(Var {
7589                    this: "TYPE".to_string(),
7590                }))),
7591                expression: None,
7592            }))));
7593        }
7594
7595        if self.match_identifier("BINARY") {
7596            // BINARY BASE64
7597            if self.match_identifier("BASE64") {
7598                return Ok(Some(Expression::QueryOption(Box::new(QueryOption {
7599                    this: Box::new(Expression::Var(Box::new(Var {
7600                        this: "BINARY BASE64".to_string(),
7601                    }))),
7602                    expression: None,
7603                }))));
7604            } else {
7605                return Ok(Some(Expression::QueryOption(Box::new(QueryOption {
7606                    this: Box::new(Expression::Var(Box::new(Var {
7607                        this: "BINARY".to_string(),
7608                    }))),
7609                    expression: None,
7610                }))));
7611            }
7612        }
7613
7614        if self.match_identifier("ELEMENTS") {
7615            // ELEMENTS [XSINIL|ABSENT]
7616            let suboption = if self.match_identifier("XSINIL") {
7617                Some("XSINIL".to_string())
7618            } else if self.match_identifier("ABSENT") {
7619                Some("ABSENT".to_string())
7620            } else {
7621                None
7622            };
7623            let option_name = match &suboption {
7624                Some(sub) => format!("ELEMENTS {}", sub),
7625                None => "ELEMENTS".to_string(),
7626            };
7627            return Ok(Some(Expression::QueryOption(Box::new(QueryOption {
7628                this: Box::new(Expression::Var(Box::new(Var { this: option_name }))),
7629                expression: None,
7630            }))));
7631        }
7632
7633        if self.match_identifier("ROOT") {
7634            let expression = if self.match_token(TokenType::LParen) {
7635                let expr = self.parse_string()?;
7636                self.expect(TokenType::RParen)?;
7637                expr
7638            } else {
7639                None
7640            };
7641            return Ok(Some(Expression::QueryOption(Box::new(QueryOption {
7642                this: Box::new(Expression::Var(Box::new(Var {
7643                    this: "ROOT".to_string(),
7644                }))),
7645                expression: expression.map(|e| Box::new(e)),
7646            }))));
7647        }
7648
7649        // No more options recognized
7650        Ok(None)
7651    }
7652
7653    /// Parse CONNECT BY clause (Oracle hierarchical queries)
7654    /// Syntax: [START WITH condition] CONNECT BY [NOCYCLE] condition [START WITH condition]
7655    /// START WITH can appear before or after CONNECT BY
7656    fn parse_connect(&mut self) -> Result<Option<Connect>> {
7657        // Check for START WITH first (can appear before CONNECT BY)
7658        let start_before = if self.match_keywords(&[TokenType::Start, TokenType::With]) {
7659            Some(self.parse_expression()?)
7660        } else {
7661            None
7662        };
7663
7664        // Check for CONNECT BY
7665        if !self.match_keywords(&[TokenType::Connect, TokenType::By]) {
7666            if start_before.is_some() {
7667                return Err(self.parse_error("START WITH without CONNECT BY"));
7668            }
7669            return Ok(None);
7670        }
7671
7672        // Check for NOCYCLE
7673        let nocycle = self.match_token(TokenType::NoCycle);
7674
7675        // Parse the CONNECT BY condition with PRIOR support
7676        let connect = self.parse_connect_expression()?;
7677
7678        // START WITH can also appear after CONNECT BY
7679        let start = if start_before.is_some() {
7680            start_before
7681        } else if self.match_keywords(&[TokenType::Start, TokenType::With]) {
7682            Some(self.parse_expression()?)
7683        } else {
7684            None
7685        };
7686
7687        Ok(Some(Connect {
7688            start,
7689            connect,
7690            nocycle,
7691        }))
7692    }
7693
7694    /// Parse expression in CONNECT BY context, treating PRIOR as prefix operator
7695    fn parse_connect_expression(&mut self) -> Result<Expression> {
7696        self.parse_connect_or()
7697    }
7698
7699    /// Parse OR expression in CONNECT BY context
7700    fn parse_connect_or(&mut self) -> Result<Expression> {
7701        let mut left = self.parse_connect_and()?;
7702
7703        while self.match_token(TokenType::Or) {
7704            let right = self.parse_connect_and()?;
7705            left = Expression::Or(Box::new(BinaryOp::new(left, right)));
7706        }
7707
7708        Ok(Self::maybe_rebalance_boolean_chain(left, false))
7709    }
7710
7711    /// Parse AND expression in CONNECT BY context
7712    fn parse_connect_and(&mut self) -> Result<Expression> {
7713        let mut left = self.parse_connect_comparison()?;
7714
7715        while self.match_token(TokenType::And) {
7716            let right = self.parse_connect_comparison()?;
7717            left = Expression::And(Box::new(BinaryOp::new(left, right)));
7718        }
7719
7720        Ok(Self::maybe_rebalance_boolean_chain(left, true))
7721    }
7722
7723    /// Parse comparison in CONNECT BY context
7724    fn parse_connect_comparison(&mut self) -> Result<Expression> {
7725        let left = self.parse_connect_primary()?;
7726
7727        if self.match_token(TokenType::Eq) {
7728            let right = self.parse_connect_primary()?;
7729            return Ok(Expression::Eq(Box::new(BinaryOp::new(left, right))));
7730        }
7731        if self.match_token(TokenType::Neq) {
7732            let right = self.parse_connect_primary()?;
7733            return Ok(Expression::Neq(Box::new(BinaryOp::new(left, right))));
7734        }
7735        if self.match_token(TokenType::Lt) {
7736            let right = self.parse_connect_primary()?;
7737            return Ok(Expression::Lt(Box::new(BinaryOp::new(left, right))));
7738        }
7739        if self.match_token(TokenType::Lte) {
7740            let right = self.parse_connect_primary()?;
7741            return Ok(Expression::Lte(Box::new(BinaryOp::new(left, right))));
7742        }
7743        if self.match_token(TokenType::Gt) {
7744            let right = self.parse_connect_primary()?;
7745            return Ok(Expression::Gt(Box::new(BinaryOp::new(left, right))));
7746        }
7747        if self.match_token(TokenType::Gte) {
7748            let right = self.parse_connect_primary()?;
7749            return Ok(Expression::Gte(Box::new(BinaryOp::new(left, right))));
7750        }
7751
7752        Ok(left)
7753    }
7754
7755    /// Parse primary in CONNECT BY context with PRIOR support
7756    fn parse_connect_primary(&mut self) -> Result<Expression> {
7757        // Handle PRIOR as prefix operator
7758        if self.match_token(TokenType::Prior) {
7759            let expr = self.parse_primary()?;
7760            return Ok(Expression::Prior(Box::new(Prior { this: expr })));
7761        }
7762
7763        if let Some(connect_by_root) = self.try_parse_connect_by_root_expression()? {
7764            return Ok(connect_by_root);
7765        }
7766
7767        self.parse_primary()
7768    }
7769
7770    /// Parse Oracle CONNECT_BY_ROOT in either supported form:
7771    /// CONNECT_BY_ROOT col
7772    /// CONNECT_BY_ROOT(col)
7773    fn try_parse_connect_by_root_expression(&mut self) -> Result<Option<Expression>> {
7774        if !(self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("CONNECT_BY_ROOT"))
7775        {
7776            return Ok(None);
7777        }
7778
7779        self.skip();
7780
7781        let this = if self.match_token(TokenType::LParen) {
7782            let expr = self.parse_expression()?;
7783            self.expect(TokenType::RParen)?;
7784            expr
7785        } else {
7786            self.parse_column()?.ok_or_else(|| {
7787                self.parse_error("Expected expression or column after CONNECT_BY_ROOT")
7788            })?
7789        };
7790
7791        Ok(Some(Expression::ConnectByRoot(Box::new(ConnectByRoot {
7792            this,
7793        }))))
7794    }
7795
7796    /// Parse MATCH_RECOGNIZE clause (Oracle/Snowflake/Presto/Trino pattern matching)
7797    /// MATCH_RECOGNIZE ( [PARTITION BY ...] [ORDER BY ...] [MEASURES ...] [rows] [after] PATTERN (...) DEFINE ... )
7798    fn parse_match_recognize(&mut self, source: Option<Expression>) -> Result<Expression> {
7799        self.expect(TokenType::LParen)?;
7800
7801        // PARTITION BY (optional)
7802        let partition_by = if self.match_keywords(&[TokenType::Partition, TokenType::By]) {
7803            Some(self.parse_expression_list()?)
7804        } else {
7805            None
7806        };
7807
7808        // ORDER BY (optional)
7809        let order_by = if self.match_keywords(&[TokenType::Order, TokenType::By]) {
7810            Some(self.parse_order_by()?.expressions)
7811        } else {
7812            None
7813        };
7814
7815        // MEASURES (optional)
7816        let measures = if self.match_token(TokenType::Measures) {
7817            Some(self.parse_match_recognize_measures()?)
7818        } else {
7819            None
7820        };
7821
7822        // Row semantics: ONE ROW PER MATCH / ALL ROWS PER MATCH
7823        let rows = self.parse_match_recognize_rows()?;
7824
7825        // AFTER MATCH SKIP
7826        let after = self.parse_match_recognize_after()?;
7827
7828        // PATTERN
7829        let pattern = if self.match_token(TokenType::Pattern) {
7830            Some(self.parse_match_recognize_pattern()?)
7831        } else {
7832            None
7833        };
7834
7835        // DEFINE
7836        let define = if self.match_token(TokenType::Define) {
7837            Some(self.parse_match_recognize_define()?)
7838        } else {
7839            None
7840        };
7841
7842        self.expect(TokenType::RParen)?;
7843
7844        // Alias is handled by the caller
7845
7846        Ok(Expression::MatchRecognize(Box::new(MatchRecognize {
7847            this: source.map(Box::new),
7848            partition_by,
7849            order_by,
7850            measures,
7851            rows,
7852            after,
7853            pattern,
7854            define,
7855            alias: None,
7856            alias_explicit_as: false,
7857        })))
7858    }
7859
7860    /// Parse MEASURES clause in MATCH_RECOGNIZE
7861    fn parse_match_recognize_measures(&mut self) -> Result<Vec<MatchRecognizeMeasure>> {
7862        let mut measures = Vec::new();
7863
7864        loop {
7865            // Check for RUNNING or FINAL
7866            let window_frame = if self.match_token(TokenType::Running) {
7867                Some(MatchRecognizeSemantics::Running)
7868            } else if self.match_token(TokenType::Final) {
7869                Some(MatchRecognizeSemantics::Final)
7870            } else {
7871                None
7872            };
7873
7874            let mut expr = self.parse_expression()?;
7875
7876            // Handle AS alias for measures
7877            if self.match_token(TokenType::As) {
7878                let alias = Identifier::new(self.expect_identifier()?);
7879                expr = Expression::Alias(Box::new(Alias::new(expr, alias)));
7880            }
7881
7882            measures.push(MatchRecognizeMeasure {
7883                this: expr,
7884                window_frame,
7885            });
7886
7887            if !self.match_token(TokenType::Comma) {
7888                break;
7889            }
7890        }
7891
7892        Ok(measures)
7893    }
7894
7895    /// Parse row semantics in MATCH_RECOGNIZE
7896    fn parse_match_recognize_rows(&mut self) -> Result<Option<MatchRecognizeRows>> {
7897        // ONE ROW PER MATCH
7898        if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("ONE") {
7899            self.skip(); // consume ONE
7900            if !self.match_token(TokenType::Row) {
7901                return Err(self.parse_error("Expected ROW after ONE"));
7902            }
7903            if !(self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("PER")) {
7904                return Err(self.parse_error("Expected PER after ONE ROW"));
7905            }
7906            self.skip(); // consume PER
7907            if !self.match_token(TokenType::Match) {
7908                return Err(self.parse_error("Expected MATCH after ONE ROW PER"));
7909            }
7910            return Ok(Some(MatchRecognizeRows::OneRowPerMatch));
7911        }
7912
7913        // ALL ROWS PER MATCH [variants]
7914        if self.match_token(TokenType::All) {
7915            if !self.match_token(TokenType::Rows) {
7916                return Err(self.parse_error("Expected ROWS after ALL"));
7917            }
7918            if !(self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("PER")) {
7919                return Err(self.parse_error("Expected PER after ALL ROWS"));
7920            }
7921            self.skip(); // consume PER
7922            if !self.match_token(TokenType::Match) {
7923                return Err(self.parse_error("Expected MATCH after ALL ROWS PER"));
7924            }
7925
7926            // Check for optional modifiers
7927            if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("SHOW") {
7928                self.skip();
7929                if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("EMPTY") {
7930                    self.skip();
7931                    if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("MATCHES") {
7932                        self.skip();
7933                        return Ok(Some(MatchRecognizeRows::AllRowsPerMatchShowEmptyMatches));
7934                    }
7935                }
7936                return Err(self.parse_error("Expected EMPTY MATCHES after SHOW"));
7937            }
7938
7939            if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("OMIT") {
7940                self.skip();
7941                if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("EMPTY") {
7942                    self.skip();
7943                    if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("MATCHES") {
7944                        self.skip();
7945                        return Ok(Some(MatchRecognizeRows::AllRowsPerMatchOmitEmptyMatches));
7946                    }
7947                }
7948                return Err(self.parse_error("Expected EMPTY MATCHES after OMIT"));
7949            }
7950
7951            if self.match_token(TokenType::With) {
7952                if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("UNMATCHED") {
7953                    self.skip();
7954                    if self.match_token(TokenType::Rows) {
7955                        return Ok(Some(MatchRecognizeRows::AllRowsPerMatchWithUnmatchedRows));
7956                    }
7957                }
7958                return Err(self.parse_error("Expected UNMATCHED ROWS after WITH"));
7959            }
7960
7961            return Ok(Some(MatchRecognizeRows::AllRowsPerMatch));
7962        }
7963
7964        Ok(None)
7965    }
7966
7967    /// Parse AFTER MATCH SKIP clause in MATCH_RECOGNIZE
7968    fn parse_match_recognize_after(&mut self) -> Result<Option<MatchRecognizeAfter>> {
7969        if !self.match_token(TokenType::After) {
7970            return Ok(None);
7971        }
7972
7973        if !self.match_token(TokenType::Match) {
7974            return Err(self.parse_error("Expected MATCH after AFTER"));
7975        }
7976
7977        // Check for SKIP (it might be an identifier)
7978        if !(self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("SKIP")) {
7979            return Err(self.parse_error("Expected SKIP after AFTER MATCH"));
7980        }
7981        self.skip(); // consume SKIP
7982
7983        // PAST LAST ROW
7984        if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("PAST") {
7985            self.skip();
7986            if self.match_token(TokenType::Last) {
7987                if self.match_token(TokenType::Row) {
7988                    return Ok(Some(MatchRecognizeAfter::PastLastRow));
7989                }
7990            }
7991            return Err(self.parse_error("Expected LAST ROW after PAST"));
7992        }
7993
7994        // TO NEXT ROW / TO FIRST x / TO LAST x
7995        if self.match_token(TokenType::To) {
7996            if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("NEXT") {
7997                self.skip();
7998                if self.match_token(TokenType::Row) {
7999                    return Ok(Some(MatchRecognizeAfter::ToNextRow));
8000                }
8001                return Err(self.parse_error("Expected ROW after NEXT"));
8002            }
8003
8004            if self.match_token(TokenType::First) {
8005                let name = self.expect_identifier()?;
8006                return Ok(Some(MatchRecognizeAfter::ToFirst(Identifier::new(name))));
8007            }
8008
8009            if self.match_token(TokenType::Last) {
8010                let name = self.expect_identifier()?;
8011                return Ok(Some(MatchRecognizeAfter::ToLast(Identifier::new(name))));
8012            }
8013
8014            return Err(self.parse_error("Expected NEXT ROW, FIRST x, or LAST x after TO"));
8015        }
8016
8017        Err(self.parse_error("Expected PAST LAST ROW or TO ... after AFTER MATCH SKIP"))
8018    }
8019
8020    /// Parse PATTERN clause in MATCH_RECOGNIZE using bracket counting
8021    fn parse_match_recognize_pattern(&mut self) -> Result<String> {
8022        self.expect(TokenType::LParen)?;
8023
8024        let mut depth = 1;
8025        let mut pattern = String::new();
8026
8027        while depth > 0 && !self.is_at_end() {
8028            let token = self.advance();
8029            match token.token_type {
8030                TokenType::LParen => {
8031                    depth += 1;
8032                    pattern.push('(');
8033                }
8034                TokenType::RParen => {
8035                    depth -= 1;
8036                    if depth > 0 {
8037                        pattern.push(')');
8038                    }
8039                }
8040                _ => {
8041                    // Pattern quantifiers (+, *, ?, {n,m}) should not have a space before them
8042                    let is_quantifier = matches!(token.text.as_str(), "+" | "*" | "?")
8043                        || token.text.starts_with('{');
8044
8045                    if !pattern.is_empty()
8046                        && !pattern.ends_with('(')
8047                        && !pattern.ends_with(' ')
8048                        && !is_quantifier
8049                    {
8050                        pattern.push(' ');
8051                    }
8052                    pattern.push_str(&token.text);
8053                }
8054            }
8055        }
8056
8057        if depth > 0 {
8058            return Err(self.parse_error("Unclosed parenthesis in PATTERN clause"));
8059        }
8060
8061        Ok(pattern.trim().to_string())
8062    }
8063
8064    /// Parse DEFINE clause in MATCH_RECOGNIZE
8065    fn parse_match_recognize_define(&mut self) -> Result<Vec<(Identifier, Expression)>> {
8066        let mut definitions = Vec::new();
8067
8068        loop {
8069            let name = Identifier::new(self.expect_identifier()?);
8070            self.expect(TokenType::As)?;
8071            let expr = self.parse_expression()?;
8072
8073            definitions.push((name, expr));
8074
8075            if !self.match_token(TokenType::Comma) {
8076                break;
8077            }
8078        }
8079
8080        Ok(definitions)
8081    }
8082
8083    /// Parse LATERAL VIEW clauses (Hive/Spark)
8084    /// Syntax: LATERAL VIEW [OUTER] generator_function(args) table_alias AS col1 [, col2, ...]
8085    fn parse_lateral_views(&mut self) -> Result<Vec<LateralView>> {
8086        let mut views = Vec::new();
8087
8088        while self.match_keywords(&[TokenType::Lateral, TokenType::View]) {
8089            // Check for OUTER keyword
8090            let outer = self.match_token(TokenType::Outer);
8091
8092            // Parse the generator function (EXPLODE, POSEXPLODE, INLINE, etc.)
8093            // This is a function call expression
8094            let this = self.parse_primary()?;
8095
8096            // Parse table alias (comes before AS)
8097            let table_alias = if self.check(TokenType::Var) && !self.check_keyword() {
8098                Some(Identifier::new(self.expect_identifier()?))
8099            } else {
8100                None
8101            };
8102
8103            // Parse column aliases after AS keyword
8104            // Supports both: AS a, b and AS (a, b)
8105            let column_aliases = if self.match_token(TokenType::As) {
8106                let mut aliases = Vec::new();
8107                // Check for parenthesized alias list: AS ("a", "b")
8108                if self.match_token(TokenType::LParen) {
8109                    loop {
8110                        aliases.push(Identifier::new(self.expect_identifier_or_keyword()?));
8111                        if !self.match_token(TokenType::Comma) {
8112                            break;
8113                        }
8114                    }
8115                    self.expect(TokenType::RParen)?;
8116                } else {
8117                    // Non-parenthesized aliases: AS a, b, c
8118                    // Use expect_identifier_or_keyword because aliases like "key", "value", "pos" may be keywords
8119                    loop {
8120                        aliases.push(Identifier::new(self.expect_identifier_or_keyword()?));
8121                        if !self.match_token(TokenType::Comma) {
8122                            break;
8123                        }
8124                        // Check if next token is still an identifier or keyword (column alias)
8125                        // vs starting a new LATERAL VIEW or other clause
8126                        if !self.is_identifier_or_keyword_token() {
8127                            break;
8128                        }
8129                        // Check for keywords that would end the column list
8130                        if self.peek().token_type == TokenType::Lateral
8131                            || self.peek().token_type == TokenType::Where
8132                            || self.peek().token_type == TokenType::Group
8133                            || self.peek().token_type == TokenType::Having
8134                            || self.peek().token_type == TokenType::Order
8135                            || self.peek().token_type == TokenType::Limit
8136                        {
8137                            break;
8138                        }
8139                    }
8140                }
8141                aliases
8142            } else {
8143                Vec::new()
8144            };
8145
8146            views.push(LateralView {
8147                this,
8148                table_alias,
8149                column_aliases,
8150                outer,
8151            });
8152        }
8153
8154        Ok(views)
8155    }
8156
8157    /// Parse named windows (WINDOW w AS (...), ...)
8158    fn parse_named_windows(&mut self) -> Result<Vec<NamedWindow>> {
8159        let mut windows = Vec::new();
8160
8161        loop {
8162            let name = self.expect_identifier()?;
8163            self.expect(TokenType::As)?;
8164            self.expect(TokenType::LParen)?;
8165
8166            // Parse optional base window name reference (e.g., w1 AS (w0 ORDER BY ...))
8167            let window_name = if (self.check(TokenType::Identifier)
8168                || self.check(TokenType::Var)
8169                || self.check(TokenType::QuotedIdentifier))
8170                && !self.check(TokenType::Partition)
8171                && !self.check(TokenType::Order)
8172                && self.peek_nth(1).map_or(true, |t| {
8173                    matches!(
8174                        t.token_type,
8175                        TokenType::Partition
8176                            | TokenType::Order
8177                            | TokenType::Rows
8178                            | TokenType::Range
8179                            | TokenType::Groups
8180                            | TokenType::RParen
8181                            | TokenType::Comma
8182                    )
8183                }) {
8184                Some(self.expect_identifier()?)
8185            } else {
8186                None
8187            };
8188
8189            // Parse window specification
8190            let partition_by = if self.match_keywords(&[TokenType::Partition, TokenType::By]) {
8191                Some(self.parse_expression_list()?)
8192            } else {
8193                None
8194            };
8195
8196            let order_by = if self.match_keywords(&[TokenType::Order, TokenType::By]) {
8197                Some(self.parse_order_by()?)
8198            } else {
8199                None
8200            };
8201
8202            let frame = self.parse_window_frame()?;
8203
8204            self.expect(TokenType::RParen)?;
8205
8206            windows.push(NamedWindow {
8207                name: Identifier::new(name),
8208                spec: Over {
8209                    window_name: window_name.map(|n| Identifier::new(n)),
8210                    partition_by: partition_by.unwrap_or_default(),
8211                    order_by: order_by.map(|o| o.expressions).unwrap_or_default(),
8212                    frame,
8213                    alias: None,
8214                },
8215            });
8216
8217            if !self.match_token(TokenType::Comma) {
8218                break;
8219            }
8220        }
8221
8222        Ok(windows)
8223    }
8224
8225    /// Parse query hint /*+ ... */
8226    fn parse_hint(&mut self) -> Result<Hint> {
8227        let token = self.advance();
8228        let hint_text = token.text.clone();
8229
8230        // For now, parse as raw hint text
8231        // More sophisticated parsing can be added later
8232        let expressions = if hint_text.is_empty() {
8233            Vec::new()
8234        } else {
8235            vec![HintExpression::Raw(hint_text)]
8236        };
8237
8238        Ok(Hint { expressions })
8239    }
8240
8241    /// Parse SAMPLE / TABLESAMPLE / USING SAMPLE clause
8242    fn parse_sample_clause(&mut self) -> Result<Option<Sample>> {
8243        // Check for USING SAMPLE (DuckDB), SAMPLE, or TABLESAMPLE
8244        let is_using_sample = if self.check(TokenType::Using)
8245            && self.current + 1 < self.tokens.len()
8246            && self.tokens[self.current + 1].token_type == TokenType::Sample
8247        {
8248            self.skip(); // consume USING
8249            self.skip(); // consume SAMPLE
8250            true
8251        } else {
8252            false
8253        };
8254
8255        let use_sample_keyword = if is_using_sample {
8256            // USING SAMPLE acts like SAMPLE
8257            true
8258        } else if self.match_token(TokenType::Sample) {
8259            true
8260        } else if self.match_token(TokenType::TableSample) {
8261            false
8262        } else {
8263            return Ok(None);
8264        };
8265
8266        // Parse sampling method if specified (BERNOULLI, SYSTEM, BLOCK, ROW, RESERVOIR)
8267        let (method, method_before_size, explicit_method) =
8268            if self.match_token(TokenType::Bernoulli) {
8269                (SampleMethod::Bernoulli, true, true)
8270            } else if self.match_token(TokenType::System) {
8271                (SampleMethod::System, true, true)
8272            } else if self.match_token(TokenType::Block) {
8273                (SampleMethod::Block, true, true)
8274            } else if self.match_token(TokenType::Row) {
8275                (SampleMethod::Row, true, true)
8276            } else if self.check_identifier("RESERVOIR") {
8277                self.skip();
8278                (SampleMethod::Reservoir, true, true)
8279            } else {
8280                // Default to BERNOULLI for both SAMPLE and TABLESAMPLE
8281                // This matches Python SQLGlot's normalization behavior
8282                (SampleMethod::Bernoulli, false, false)
8283            };
8284
8285        // Parse size (can be in parentheses)
8286        let has_paren = self.match_token(TokenType::LParen);
8287
8288        // Check for BUCKET syntax: TABLESAMPLE (BUCKET 1 OUT OF 5 ON x)
8289        if self.match_identifier("BUCKET") {
8290            let bucket_numerator = self.parse_primary()?;
8291            self.match_identifier("OUT");
8292            self.match_token(TokenType::Of); // OF is a keyword token
8293            let bucket_denominator = self.parse_primary()?;
8294            let bucket_field = if self.match_token(TokenType::On) {
8295                Some(Box::new(self.parse_primary()?))
8296            } else {
8297                None
8298            };
8299            if has_paren {
8300                self.expect(TokenType::RParen)?;
8301            }
8302            return Ok(Some(Sample {
8303                method: SampleMethod::Bucket,
8304                size: bucket_numerator.clone(),
8305                seed: None,
8306                offset: None,
8307                unit_after_size: false,
8308                use_sample_keyword,
8309                explicit_method: true,     // BUCKET is always explicit
8310                method_before_size: false, // BUCKET appears inside parens
8311                use_seed_keyword: false,
8312                bucket_numerator: Some(Box::new(bucket_numerator)),
8313                bucket_denominator: Some(Box::new(bucket_denominator)),
8314                bucket_field,
8315                is_using_sample,
8316                is_percent: false,
8317                suppress_method_output: false,
8318            }));
8319        }
8320
8321        // Use parse_unary to avoid consuming PERCENT as modulo operator
8322        let size = self.parse_unary()?;
8323
8324        // Check for PERCENT/ROWS suffix after size (if not already part of the number)
8325        // Both "%" and "PERCENT" tokens map to TokenType::Percent - accept both as PERCENT modifier
8326        let (method, unit_after_size, is_percent) = if self.check(TokenType::Percent) {
8327            self.skip(); // consume PERCENT or %
8328                            // If method was already explicitly specified (e.g., SYSTEM), keep it
8329                            // PERCENT here is just the unit, not the sampling method
8330            if method_before_size {
8331                (method, true, true)
8332            } else {
8333                (SampleMethod::Percent, true, true)
8334            }
8335        } else if self.match_token(TokenType::Rows) {
8336            // If method was already explicitly specified, keep it
8337            if method_before_size {
8338                (method, true, false)
8339            } else {
8340                (SampleMethod::Row, true, false)
8341            }
8342        } else {
8343            // No explicit unit after size - preserve the original method
8344            (method, false, false)
8345        };
8346
8347        if has_paren {
8348            self.expect(TokenType::RParen)?;
8349        }
8350
8351        // DuckDB USING SAMPLE: method and optional seed can come in parens after size
8352        // e.g., "10 PERCENT (bernoulli)" or "10% (system, 377)"
8353        // DuckDB USING SAMPLE: method and optional seed can come in parens after size
8354        // e.g., "10 PERCENT (bernoulli)" or "10% (system, 377)"
8355        let (method, seed, use_seed_keyword, explicit_method) =
8356            if is_using_sample && self.check(TokenType::LParen) {
8357                self.skip(); // consume LParen
8358                                // Parse method name as identifier or keyword token
8359                                // BERNOULLI, SYSTEM, RESERVOIR can be tokenized as keywords, not identifiers
8360                let method_from_parens =
8361                    if self.check_identifier("BERNOULLI") || self.check(TokenType::Bernoulli) {
8362                        self.skip();
8363                        Some(SampleMethod::Bernoulli)
8364                    } else if self.check_identifier("SYSTEM") || self.check(TokenType::System) {
8365                        self.skip();
8366                        Some(SampleMethod::System)
8367                    } else if self.check_identifier("RESERVOIR") {
8368                        self.skip();
8369                        Some(SampleMethod::Reservoir)
8370                    } else {
8371                        None
8372                    };
8373                // Optional seed after comma
8374                let seed = if self.match_token(TokenType::Comma) {
8375                    Some(self.parse_expression()?)
8376                } else {
8377                    None
8378                };
8379                self.expect(TokenType::RParen)?;
8380                let final_method = method_from_parens.unwrap_or(method);
8381                (final_method, seed, false, true)
8382            } else {
8383                // Parse optional SEED / REPEATABLE
8384                let (seed, use_seed_keyword) = if self.match_token(TokenType::Seed) {
8385                    self.expect(TokenType::LParen)?;
8386                    let seed_value = self.parse_expression()?;
8387                    self.expect(TokenType::RParen)?;
8388                    (Some(seed_value), true)
8389                } else if self.match_token(TokenType::Repeatable) {
8390                    self.expect(TokenType::LParen)?;
8391                    let seed_value = self.parse_expression()?;
8392                    self.expect(TokenType::RParen)?;
8393                    (Some(seed_value), false)
8394                } else {
8395                    (None, false)
8396                };
8397                let explicit_method = explicit_method || unit_after_size;
8398                (method, seed, use_seed_keyword, explicit_method)
8399            };
8400
8401        // For DuckDB USING SAMPLE: apply default methods
8402        // - bare number -> RESERVOIR, ROWS
8403        // - percent -> SYSTEM, PERCENT
8404        let (method, unit_after_size) = if is_using_sample && !explicit_method {
8405            // No explicit method - apply defaults
8406            (SampleMethod::Reservoir, false) // default: RESERVOIR with ROWS
8407        } else if is_using_sample && unit_after_size && !method_before_size {
8408            // Unit was specified after size (e.g., "10 PERCENT") but no method before
8409            // Check if method was set in post-parens
8410            if matches!(method, SampleMethod::Percent) {
8411                // "10%" or "10 PERCENT" without method -> SYSTEM
8412                (SampleMethod::System, true)
8413            } else if matches!(method, SampleMethod::Row) {
8414                // "50 ROWS" without method -> RESERVOIR
8415                (SampleMethod::Reservoir, true)
8416            } else {
8417                (method, unit_after_size)
8418            }
8419        } else {
8420            (method, unit_after_size)
8421        };
8422
8423        // method_before_size: true for USING SAMPLE - we normalize to method-before-size format
8424        // e.g., "10 PERCENT (bernoulli)" becomes "BERNOULLI (10 PERCENT)"
8425        Ok(Some(Sample {
8426            method,
8427            size,
8428            seed,
8429            offset: None,
8430            unit_after_size,
8431            use_sample_keyword,
8432            explicit_method: true,    // For USING SAMPLE, always explicit
8433            method_before_size: true, // Normalize to method-before-size format
8434            use_seed_keyword,
8435            bucket_numerator: None,
8436            bucket_denominator: None,
8437            bucket_field: None,
8438            is_using_sample,
8439            is_percent,
8440            suppress_method_output: false,
8441        }))
8442    }
8443
8444    /// Parse table-level TABLESAMPLE/SAMPLE: TABLESAMPLE/SAMPLE METHOD(size [PERCENT|ROWS])
8445    /// e.g., TABLESAMPLE RESERVOIR(20%), SAMPLE BERNOULLI(10 PERCENT), SAMPLE ROW(0)
8446    fn parse_table_level_sample(&mut self) -> Result<Option<Sample>> {
8447        // Accept both TABLESAMPLE and SAMPLE (Snowflake supports both)
8448        let use_sample_keyword = if self.match_token(TokenType::Sample) {
8449            true
8450        } else if self.match_token(TokenType::TableSample) {
8451            false
8452        } else {
8453            return Ok(None);
8454        };
8455        // Track which keyword was used for identity output
8456        let _ = use_sample_keyword; // Used below for is_using_sample field
8457
8458        // Teradata: SAMPLE 5 or SAMPLE 0.33, .25, .1 (no parentheses)
8459        if matches!(
8460            self.config.dialect,
8461            Some(crate::dialects::DialectType::Teradata)
8462        ) && use_sample_keyword
8463            && !self.check(TokenType::LParen)
8464        {
8465            let mut expressions = vec![self.parse_unary()?];
8466            while self.match_token(TokenType::Comma) {
8467                expressions.push(self.parse_unary()?);
8468            }
8469            let size = if expressions.len() == 1 {
8470                expressions.into_iter().next().unwrap()
8471            } else {
8472                Expression::Tuple(Box::new(Tuple { expressions }))
8473            };
8474            return Ok(Some(Sample {
8475                method: SampleMethod::Percent,
8476                size,
8477                seed: None,
8478                offset: None,
8479                unit_after_size: false,
8480                use_sample_keyword,
8481                explicit_method: false,
8482                method_before_size: false,
8483                use_seed_keyword: false,
8484                bucket_numerator: None,
8485                bucket_denominator: None,
8486                bucket_field: None,
8487                is_using_sample: false,
8488                is_percent: false,
8489                suppress_method_output: false,
8490            }));
8491        }
8492
8493        // ClickHouse: SAMPLE 0.1 [OFFSET 0.2] (no parentheses)
8494        if matches!(
8495            self.config.dialect,
8496            Some(crate::dialects::DialectType::ClickHouse)
8497        ) && use_sample_keyword
8498            && !self.check(TokenType::LParen)
8499        {
8500            let size = self.parse_expression()?;
8501            let offset = if self.match_token(TokenType::Offset) {
8502                Some(self.parse_expression()?)
8503            } else {
8504                None
8505            };
8506            return Ok(Some(Sample {
8507                method: SampleMethod::Bernoulli,
8508                size,
8509                seed: None,
8510                offset,
8511                unit_after_size: false,
8512                use_sample_keyword,
8513                explicit_method: false,
8514                method_before_size: false,
8515                use_seed_keyword: false,
8516                bucket_numerator: None,
8517                bucket_denominator: None,
8518                bucket_field: None,
8519                is_using_sample: false,
8520                is_percent: false,
8521                suppress_method_output: false,
8522            }));
8523        }
8524
8525        // Parse method name (optional for table-level TABLESAMPLE)
8526        let (method, explicit_method, method_before_size) = if self.check_identifier("RESERVOIR") {
8527            self.skip();
8528            (SampleMethod::Reservoir, true, true)
8529        } else if self.match_token(TokenType::Bernoulli) {
8530            (SampleMethod::Bernoulli, true, true)
8531        } else if self.match_token(TokenType::System) {
8532            (SampleMethod::System, true, true)
8533        } else if self.match_token(TokenType::Block) {
8534            (SampleMethod::Block, true, true)
8535        } else if self.match_token(TokenType::Row) {
8536            (SampleMethod::Row, true, true)
8537        } else {
8538            // No explicit method - default to Bernoulli internally but track as not explicit
8539            (SampleMethod::Bernoulli, false, false)
8540        };
8541
8542        // Parse (size [PERCENT|ROWS])
8543        self.expect(TokenType::LParen)?;
8544
8545        // Check for BUCKET syntax: TABLESAMPLE (BUCKET 1 OUT OF 5 [ON col])
8546        if self.match_identifier("BUCKET") {
8547            let bucket_numerator = self.parse_primary()?;
8548            self.match_identifier("OUT");
8549            self.match_token(TokenType::Of);
8550            let bucket_denominator = self.parse_primary()?;
8551            let bucket_field = if self.match_token(TokenType::On) {
8552                Some(Box::new(self.parse_primary()?))
8553            } else {
8554                None
8555            };
8556            self.expect(TokenType::RParen)?;
8557            return Ok(Some(Sample {
8558                method: SampleMethod::Bucket,
8559                size: bucket_numerator.clone(),
8560                seed: None,
8561                offset: None,
8562                unit_after_size: false,
8563                use_sample_keyword,
8564                explicit_method: true,
8565                method_before_size: false,
8566                use_seed_keyword: false,
8567                bucket_numerator: Some(Box::new(bucket_numerator)),
8568                bucket_denominator: Some(Box::new(bucket_denominator)),
8569                bucket_field,
8570                is_using_sample: false,
8571                is_percent: false,
8572                suppress_method_output: false,
8573            }));
8574        }
8575
8576        let size = self.parse_unary()?;
8577
8578        // Check for PERCENT/ROWS suffix or % symbol
8579        let (method, unit_after_size, is_percent) =
8580            if self.check(TokenType::Percent) && self.peek().text.eq_ignore_ascii_case("PERCENT") {
8581                self.skip();
8582                // If no explicit method, use Percent to represent "PERCENT" unit
8583                if explicit_method {
8584                    (method, true, true)
8585                } else {
8586                    (SampleMethod::Percent, true, true)
8587                }
8588            } else if self.match_token(TokenType::Rows) {
8589                // If no explicit method, use Row to represent "ROWS" unit
8590                if explicit_method {
8591                    (method, true, false)
8592                } else {
8593                    (SampleMethod::Row, true, false)
8594                }
8595            } else if self.check(TokenType::Percent) && self.peek().text == "%" {
8596                // 20% -> consume the %, treat as PERCENT unit
8597                self.skip();
8598                if explicit_method {
8599                    (method, true, true)
8600                } else {
8601                    (SampleMethod::Percent, true, true)
8602                }
8603            } else {
8604                (method, false, false)
8605            };
8606
8607        self.expect(TokenType::RParen)?;
8608
8609        // Optional SEED/REPEATABLE
8610        let (seed, use_seed_keyword) = if self.match_token(TokenType::Seed) {
8611            self.expect(TokenType::LParen)?;
8612            let seed_value = self.parse_expression()?;
8613            self.expect(TokenType::RParen)?;
8614            (Some(seed_value), true)
8615        } else if self.match_token(TokenType::Repeatable) {
8616            self.expect(TokenType::LParen)?;
8617            let seed_value = self.parse_expression()?;
8618            self.expect(TokenType::RParen)?;
8619            (Some(seed_value), false)
8620        } else {
8621            (None, false)
8622        };
8623
8624        Ok(Some(Sample {
8625            method,
8626            size,
8627            seed,
8628            offset: None,
8629            unit_after_size,
8630            use_sample_keyword,
8631            explicit_method,
8632            method_before_size,
8633            use_seed_keyword,
8634            bucket_numerator: None,
8635            bucket_denominator: None,
8636            bucket_field: None,
8637            is_using_sample: false, // table-level uses TABLESAMPLE/SAMPLE keyword, not USING SAMPLE
8638            is_percent,
8639            suppress_method_output: false,
8640        }))
8641    }
8642
8643    /// Parse set operations (UNION, INTERSECT, EXCEPT)
8644    fn parse_set_operation(&mut self, left: Expression) -> Result<Expression> {
8645        // Check for BigQuery set operation modifiers BEFORE the set operation keyword
8646        // Pattern: SELECT ... [INNER|LEFT|RIGHT|FULL] UNION/INTERSECT/EXCEPT ...
8647        let (side, kind) = self.parse_set_operation_side_kind();
8648
8649        // Capture leading comments from the set operation keyword token (e.g., /*x*/ before UNION).
8650        // These comments appeared on a new line between the left SELECT and the set operation keyword.
8651        let set_op_leading_comments = if self.check(TokenType::Union)
8652            || self.check(TokenType::Intersect)
8653            || self.check(TokenType::Except)
8654        {
8655            self.current_leading_comments().to_vec()
8656        } else {
8657            Vec::new()
8658        };
8659
8660        // Wrap left expression with comments if needed
8661        let left = if !set_op_leading_comments.is_empty() {
8662            Expression::Annotated(Box::new(Annotated {
8663                this: left,
8664                trailing_comments: set_op_leading_comments,
8665            }))
8666        } else {
8667            left
8668        };
8669
8670        if self.match_token(TokenType::Union) {
8671            let all = self.match_token(TokenType::All);
8672            let distinct = if !all {
8673                self.match_token(TokenType::Distinct)
8674            } else {
8675                false
8676            };
8677
8678            // Parse STRICT CORRESPONDING, CORRESPONDING, BY NAME modifiers
8679            let (by_name, strict, corresponding, on_columns) =
8680                self.parse_set_operation_corresponding()?;
8681
8682            // If CORRESPONDING (without STRICT) is present and no explicit side/kind, default kind to INNER
8683            // STRICT CORRESPONDING does NOT set kind to INNER
8684            let kind = if corresponding && !strict && side.is_none() && kind.is_none() {
8685                Some("INNER".to_string())
8686            } else {
8687                kind
8688            };
8689
8690            let right = self.parse_select_or_paren_select()?;
8691            // Check for chained set operations first
8692            let mut result = Expression::Union(Box::new(Union {
8693                left,
8694                right,
8695                all,
8696                distinct,
8697                with: None,
8698                order_by: None,
8699                limit: None,
8700                offset: None,
8701                distribute_by: None,
8702                sort_by: None,
8703                cluster_by: None,
8704                by_name,
8705                side,
8706                kind,
8707                corresponding,
8708                strict,
8709                on_columns,
8710            }));
8711            result = self.parse_set_operation(result)?;
8712            // Parse ORDER BY, LIMIT, OFFSET for the outermost set operation
8713            self.parse_set_operation_modifiers(&mut result)?;
8714            Ok(result)
8715        } else if self.match_token(TokenType::Intersect) {
8716            let all = self.match_token(TokenType::All);
8717            let distinct = if !all {
8718                self.match_token(TokenType::Distinct)
8719            } else {
8720                false
8721            };
8722
8723            // Parse STRICT CORRESPONDING, CORRESPONDING, BY NAME modifiers
8724            let (by_name, strict, corresponding, on_columns) =
8725                self.parse_set_operation_corresponding()?;
8726
8727            // If CORRESPONDING (without STRICT) is present and no explicit side/kind, default kind to INNER
8728            // STRICT CORRESPONDING does NOT set kind to INNER
8729            let kind = if corresponding && !strict && side.is_none() && kind.is_none() {
8730                Some("INNER".to_string())
8731            } else {
8732                kind
8733            };
8734
8735            let right = self.parse_select_or_paren_select()?;
8736            let mut result = Expression::Intersect(Box::new(Intersect {
8737                left,
8738                right,
8739                all,
8740                distinct,
8741                with: None,
8742                order_by: None,
8743                limit: None,
8744                offset: None,
8745                distribute_by: None,
8746                sort_by: None,
8747                cluster_by: None,
8748                by_name,
8749                side,
8750                kind,
8751                corresponding,
8752                strict,
8753                on_columns,
8754            }));
8755            result = self.parse_set_operation(result)?;
8756            self.parse_set_operation_modifiers(&mut result)?;
8757            Ok(result)
8758        } else if self.match_token(TokenType::Except) {
8759            let all = self.match_token(TokenType::All);
8760            let distinct = if !all {
8761                self.match_token(TokenType::Distinct)
8762            } else {
8763                false
8764            };
8765
8766            // Parse STRICT CORRESPONDING, CORRESPONDING, BY NAME modifiers
8767            let (by_name, strict, corresponding, on_columns) =
8768                self.parse_set_operation_corresponding()?;
8769
8770            // If CORRESPONDING (without STRICT) is present and no explicit side/kind, default kind to INNER
8771            // STRICT CORRESPONDING does NOT set kind to INNER
8772            let kind = if corresponding && !strict && side.is_none() && kind.is_none() {
8773                Some("INNER".to_string())
8774            } else {
8775                kind
8776            };
8777
8778            let right = self.parse_select_or_paren_select()?;
8779            let mut result = Expression::Except(Box::new(Except {
8780                left,
8781                right,
8782                all,
8783                distinct,
8784                with: None,
8785                order_by: None,
8786                limit: None,
8787                offset: None,
8788                distribute_by: None,
8789                sort_by: None,
8790                cluster_by: None,
8791                by_name,
8792                side,
8793                kind,
8794                corresponding,
8795                strict,
8796                on_columns,
8797            }));
8798            result = self.parse_set_operation(result)?;
8799            self.parse_set_operation_modifiers(&mut result)?;
8800            Ok(result)
8801        } else if side.is_some() || kind.is_some() {
8802            // We parsed side/kind but didn't find a set operation - this is an error
8803            Err(self
8804                .parse_error("Expected UNION, INTERSECT, or EXCEPT after set operation modifier"))
8805        } else {
8806            Ok(left)
8807        }
8808    }
8809
8810    /// Parse BigQuery set operation side (LEFT, RIGHT, FULL) and kind (INNER)
8811    /// These modifiers appear BEFORE the UNION/INTERSECT/EXCEPT keyword
8812    fn parse_set_operation_side_kind(&mut self) -> (Option<String>, Option<String>) {
8813        let mut side = None;
8814        let mut kind = None;
8815
8816        // Check for side: LEFT, RIGHT, FULL (reusing join side tokens)
8817        if self.check(TokenType::Left)
8818            || self.check(TokenType::Right)
8819            || self.check(TokenType::Full)
8820        {
8821            // Only consume if followed by UNION/INTERSECT/EXCEPT (or INNER which would be followed by them)
8822            let saved = self.current;
8823            let side_token = self.advance();
8824            let side_text = side_token.text.to_ascii_uppercase();
8825
8826            // Check if followed by set operation or INNER
8827            if self.check(TokenType::Union)
8828                || self.check(TokenType::Intersect)
8829                || self.check(TokenType::Except)
8830                || self.check(TokenType::Inner)
8831            {
8832                side = Some(side_text);
8833            } else {
8834                // Not a set operation modifier, backtrack
8835                self.current = saved;
8836                return (None, None);
8837            }
8838        }
8839
8840        // Check for kind: INNER
8841        if self.check(TokenType::Inner) {
8842            let saved = self.current;
8843            self.skip(); // consume INNER
8844
8845            // Check if followed by set operation
8846            if self.check(TokenType::Union)
8847                || self.check(TokenType::Intersect)
8848                || self.check(TokenType::Except)
8849            {
8850                kind = Some("INNER".to_string());
8851            } else {
8852                // Not a set operation modifier, backtrack
8853                self.current = saved;
8854                if side.is_some() {
8855                    // We already consumed a side token, need to backtrack that too
8856                    self.current = saved - 1;
8857                }
8858                return (None, None);
8859            }
8860        }
8861
8862        (side, kind)
8863    }
8864
8865    /// Parse CORRESPONDING/STRICT CORRESPONDING/BY NAME modifiers after ALL/DISTINCT
8866    /// Returns (by_name, strict, corresponding, on_columns)
8867    fn parse_set_operation_corresponding(&mut self) -> Result<(bool, bool, bool, Vec<Expression>)> {
8868        let mut by_name = false;
8869        let mut strict = false;
8870        let mut corresponding = false;
8871        let mut on_columns = Vec::new();
8872
8873        // Check for BY NAME (DuckDB style)
8874        if self.match_token(TokenType::By) && self.match_identifier("NAME") {
8875            by_name = true;
8876        }
8877        // Check for STRICT CORRESPONDING (BigQuery style)
8878        else if self.match_identifier("STRICT") {
8879            if self.match_identifier("CORRESPONDING") {
8880                strict = true;
8881                corresponding = true;
8882            } else {
8883                // STRICT without CORRESPONDING - backtrack
8884                self.current -= 1;
8885            }
8886        }
8887        // Check for CORRESPONDING (BigQuery style)
8888        else if self.match_identifier("CORRESPONDING") {
8889            corresponding = true;
8890        }
8891
8892        // If CORRESPONDING is set, check for BY (columns)
8893        if corresponding && self.match_token(TokenType::By) {
8894            self.expect(TokenType::LParen)?;
8895            on_columns = self
8896                .parse_identifier_list()?
8897                .into_iter()
8898                .map(|id| {
8899                    Expression::boxed_column(Column {
8900                        name: id,
8901                        table: None,
8902                        join_mark: false,
8903                        trailing_comments: Vec::new(),
8904                        span: None,
8905                        inferred_type: None,
8906                    })
8907                })
8908                .collect();
8909            self.expect(TokenType::RParen)?;
8910        }
8911
8912        Ok((by_name, strict, corresponding, on_columns))
8913    }
8914
8915    /// Parse ORDER BY, LIMIT, OFFSET modifiers for set operations
8916    fn parse_set_operation_modifiers(&mut self, expr: &mut Expression) -> Result<()> {
8917        // Parse ORDER BY
8918        let order_by = if self.match_token(TokenType::Order) {
8919            self.expect(TokenType::By)?;
8920            Some(self.parse_order_by()?)
8921        } else {
8922            None
8923        };
8924
8925        // Parse LIMIT
8926        let limit = if self.match_token(TokenType::Limit) {
8927            Some(Box::new(self.parse_expression()?))
8928        } else {
8929            None
8930        };
8931
8932        // Parse OFFSET
8933        let offset = if self.match_token(TokenType::Offset) {
8934            Some(Box::new(self.parse_expression()?))
8935        } else {
8936            None
8937        };
8938
8939        // Apply modifiers to the outermost set operation
8940        match expr {
8941            Expression::Union(ref mut union) => {
8942                if order_by.is_some() {
8943                    union.order_by = order_by;
8944                }
8945                if limit.is_some() {
8946                    union.limit = limit;
8947                }
8948                if offset.is_some() {
8949                    union.offset = offset;
8950                }
8951            }
8952            Expression::Intersect(ref mut intersect) => {
8953                if order_by.is_some() {
8954                    intersect.order_by = order_by;
8955                }
8956                if limit.is_some() {
8957                    intersect.limit = limit;
8958                }
8959                if offset.is_some() {
8960                    intersect.offset = offset;
8961                }
8962            }
8963            Expression::Except(ref mut except) => {
8964                if order_by.is_some() {
8965                    except.order_by = order_by;
8966                }
8967                if limit.is_some() {
8968                    except.limit = limit;
8969                }
8970                if offset.is_some() {
8971                    except.offset = offset;
8972                }
8973            }
8974            _ => {}
8975        }
8976        Ok(())
8977    }
8978
8979    /// Parse either a SELECT statement or a parenthesized SELECT/set operation
8980    fn parse_select_or_paren_select(&mut self) -> Result<Expression> {
8981        if self.match_token(TokenType::LParen) {
8982            // Could be (SELECT ...) or ((SELECT ...) UNION ...) or (FROM ...) for DuckDB
8983            if self.check(TokenType::Select)
8984                || self.check(TokenType::With)
8985                || self.check(TokenType::From)
8986            {
8987                let query = self.parse_statement()?;
8988                self.expect(TokenType::RParen)?;
8989                // Handle optional alias after subquery: (SELECT 1) AS a
8990                let alias = if self.match_token(TokenType::As) {
8991                    Some(Identifier::new(self.expect_identifier()?))
8992                } else {
8993                    None
8994                };
8995                // Wrap in Subquery to preserve parentheses
8996                Ok(Expression::Subquery(Box::new(Subquery {
8997                    this: query,
8998                    alias,
8999                    column_aliases: Vec::new(),
9000                    order_by: None,
9001                    limit: None,
9002                    offset: None,
9003                    lateral: false,
9004                    modifiers_inside: false,
9005                    trailing_comments: Vec::new(),
9006                    distribute_by: None,
9007                    sort_by: None,
9008                    cluster_by: None,
9009                    inferred_type: None,
9010                })))
9011            } else if self.check(TokenType::LParen) {
9012                // Nested parentheses like ((SELECT ...))
9013                let inner = self.parse_select_or_paren_select()?;
9014                // Check for set operations inside the parens
9015                let result = self.parse_set_operation(inner)?;
9016                self.expect(TokenType::RParen)?;
9017                // Handle optional alias after subquery
9018                let alias = if self.match_token(TokenType::As) {
9019                    Some(Identifier::new(self.expect_identifier()?))
9020                } else {
9021                    None
9022                };
9023                // Wrap in Subquery to preserve parentheses
9024                Ok(Expression::Subquery(Box::new(Subquery {
9025                    this: result,
9026                    alias,
9027                    column_aliases: Vec::new(),
9028                    order_by: None,
9029                    limit: None,
9030                    offset: None,
9031                    lateral: false,
9032                    modifiers_inside: false,
9033                    trailing_comments: Vec::new(),
9034                    distribute_by: None,
9035                    sort_by: None,
9036                    cluster_by: None,
9037                    inferred_type: None,
9038                })))
9039            } else {
9040                Err(self.parse_error("Expected SELECT or ( after ("))
9041            }
9042        } else if self.check(TokenType::From) {
9043            // DuckDB FROM-first syntax without parentheses: ... UNION FROM t
9044            self.parse_from_first_query()
9045        } else if self.check(TokenType::With) {
9046            // WITH CTE as right-hand side of UNION/INTERSECT/EXCEPT
9047            self.parse_statement()
9048        } else {
9049            self.parse_select()
9050        }
9051    }
9052
9053    /// Parse INSERT statement
9054    fn parse_insert(&mut self) -> Result<Expression> {
9055        let insert_token = self.expect(TokenType::Insert)?;
9056        let leading_comments = insert_token.comments;
9057
9058        // Parse query hint /*+ ... */ if present (Oracle: INSERT /*+ APPEND */ INTO ...)
9059        let hint = if self.check(TokenType::Hint) {
9060            Some(self.parse_hint()?)
9061        } else {
9062            None
9063        };
9064
9065        // Handle SQLite conflict action: INSERT OR ABORT|FAIL|IGNORE|REPLACE|ROLLBACK INTO
9066        let conflict_action = if self.match_token(TokenType::Or) {
9067            if self.match_identifier("ABORT") {
9068                Some("ABORT".to_string())
9069            } else if self.match_identifier("FAIL") {
9070                Some("FAIL".to_string())
9071            } else if self.match_token(TokenType::Ignore) {
9072                Some("IGNORE".to_string())
9073            } else if self.match_token(TokenType::Replace) {
9074                Some("REPLACE".to_string())
9075            } else if self.match_token(TokenType::Rollback) {
9076                Some("ROLLBACK".to_string())
9077            } else {
9078                return Err(self.parse_error(
9079                    "Expected ABORT, FAIL, IGNORE, REPLACE, or ROLLBACK after INSERT OR",
9080                ));
9081            }
9082        } else {
9083            None
9084        };
9085
9086        // Handle INSERT IGNORE (MySQL)
9087        let ignore = conflict_action.is_none() && self.match_token(TokenType::Ignore);
9088
9089        // Handle OVERWRITE for Hive/Spark: INSERT OVERWRITE TABLE ...
9090        let overwrite = self.match_token(TokenType::Overwrite);
9091
9092        // Handle Oracle multi-table INSERT: INSERT ALL/FIRST ...
9093        // Must check before OVERWRITE handling since these are mutually exclusive
9094        if !overwrite && (self.match_token(TokenType::All) || self.match_token(TokenType::First)) {
9095            if let Some(multi_insert) = self.parse_multitable_inserts(leading_comments.clone())? {
9096                return Ok(multi_insert);
9097            }
9098        }
9099
9100        // Handle INTO or TABLE (OVERWRITE requires TABLE, INTO is standard)
9101        // Also handle INSERT OVERWRITE [LOCAL] DIRECTORY 'path'
9102        let local_directory = overwrite && self.match_token(TokenType::Local);
9103        let is_directory = (overwrite || local_directory) && self.match_identifier("DIRECTORY");
9104
9105        if is_directory {
9106            // INSERT OVERWRITE [LOCAL] DIRECTORY 'path' [ROW FORMAT ...] SELECT ...
9107            let path = self.expect_string()?;
9108            // Parse optional ROW FORMAT clause
9109            let row_format = if self.match_keywords(&[TokenType::Row, TokenType::Format]) {
9110                // ROW FORMAT DELIMITED ...
9111                let delimited = self.match_identifier("DELIMITED");
9112                let mut fields_terminated_by = None;
9113                let mut collection_items_terminated_by = None;
9114                let mut map_keys_terminated_by = None;
9115                let mut lines_terminated_by = None;
9116                let mut null_defined_as = None;
9117
9118                // Parse the various TERMINATED BY clauses
9119                loop {
9120                    if self.match_identifier("FIELDS") || self.match_identifier("FIELD") {
9121                        self.match_identifier("TERMINATED");
9122                        self.match_token(TokenType::By);
9123                        fields_terminated_by = Some(self.expect_string()?);
9124                    } else if self.match_identifier("COLLECTION") {
9125                        self.match_identifier("ITEMS");
9126                        self.match_identifier("TERMINATED");
9127                        self.match_token(TokenType::By);
9128                        collection_items_terminated_by = Some(self.expect_string()?);
9129                    } else if self.match_identifier("MAP") {
9130                        self.match_identifier("KEYS");
9131                        self.match_identifier("TERMINATED");
9132                        self.match_token(TokenType::By);
9133                        map_keys_terminated_by = Some(self.expect_string()?);
9134                    } else if self.match_identifier("LINES") {
9135                        self.match_identifier("TERMINATED");
9136                        self.match_token(TokenType::By);
9137                        lines_terminated_by = Some(self.expect_string()?);
9138                    } else if self.match_token(TokenType::Null) {
9139                        self.match_identifier("DEFINED");
9140                        self.match_token(TokenType::As);
9141                        null_defined_as = Some(self.expect_string()?);
9142                    } else {
9143                        break;
9144                    }
9145                }
9146
9147                Some(RowFormat {
9148                    delimited,
9149                    fields_terminated_by,
9150                    collection_items_terminated_by,
9151                    map_keys_terminated_by,
9152                    lines_terminated_by,
9153                    null_defined_as,
9154                })
9155            } else {
9156                None
9157            };
9158
9159            // Parse optional STORED AS clause
9160            let stored_as = if self.match_identifier("STORED") {
9161                self.expect(TokenType::As)?;
9162                Some(self.expect_identifier()?)
9163            } else {
9164                None
9165            };
9166
9167            // Parse the SELECT query
9168            let query = self.parse_statement()?;
9169
9170            return Ok(Expression::Insert(Box::new(Insert {
9171                table: TableRef::new(""),
9172                columns: Vec::new(),
9173                values: Vec::new(),
9174                query: Some(query),
9175                overwrite,
9176                partition: Vec::new(),
9177                directory: Some(DirectoryInsert {
9178                    local: local_directory,
9179                    path,
9180                    row_format,
9181                    stored_as,
9182                }),
9183                returning: Vec::new(),
9184                output: None,
9185                on_conflict: None,
9186                leading_comments,
9187                if_exists: false,
9188                with: None,
9189                ignore,
9190                source_alias: None,
9191                alias: None,
9192                alias_explicit_as: false,
9193                default_values: false,
9194                by_name: false,
9195                conflict_action: conflict_action.clone(),
9196                is_replace: false,
9197                replace_where: None,
9198                source: None,
9199                hint: hint.clone(),
9200                function_target: None,
9201                partition_by: None,
9202                settings: Vec::new(),
9203            })));
9204        }
9205
9206        if overwrite {
9207            // OVERWRITE is typically followed by TABLE
9208            self.match_token(TokenType::Table);
9209        } else {
9210            self.expect(TokenType::Into)?;
9211            // Optional TABLE keyword after INTO
9212            self.match_token(TokenType::Table);
9213        }
9214
9215        // ClickHouse: INSERT INTO [TABLE] FUNCTION func_name(args...)
9216        let mut function_target: Option<Box<Expression>> = None;
9217        if self.match_token(TokenType::Function) {
9218            // Parse function call: func_name(args...)
9219            let func_name = self.expect_identifier_or_keyword()?;
9220            self.expect(TokenType::LParen)?;
9221            let args = if self.check(TokenType::RParen) {
9222                Vec::new()
9223            } else {
9224                self.parse_expression_list()?
9225            };
9226            self.expect(TokenType::RParen)?;
9227            function_target = Some(Box::new(Expression::Function(Box::new(Function {
9228                name: func_name,
9229                args,
9230                distinct: false,
9231                trailing_comments: Vec::new(),
9232                use_bracket_syntax: false,
9233                no_parens: false,
9234                quoted: false,
9235                span: None,
9236                inferred_type: None,
9237            }))));
9238        }
9239
9240        let table_name = if function_target.is_some() {
9241            // For FUNCTION targets, use empty table name
9242            Identifier::new(String::new())
9243        } else {
9244            // Allow keywords (like TABLE) as table names in INSERT statements
9245            self.expect_identifier_or_keyword_with_quoted()?
9246        };
9247        // Handle qualified table names like a.b
9248        let table = if self.match_token(TokenType::Dot) {
9249            let schema = table_name;
9250            let name = self.expect_identifier_or_keyword_with_quoted()?;
9251            let trailing_comments = self.previous_trailing_comments().to_vec();
9252            TableRef {
9253                name,
9254                schema: Some(schema),
9255                catalog: None,
9256                alias: None,
9257                alias_explicit_as: false,
9258                column_aliases: Vec::new(),
9259                trailing_comments,
9260                when: None,
9261                only: false,
9262                final_: false,
9263                table_sample: None,
9264                hints: Vec::new(),
9265                system_time: None,
9266                partitions: Vec::new(),
9267                identifier_func: None,
9268                changes: None,
9269                version: None,
9270                span: None,
9271            }
9272        } else {
9273            let trailing_comments = self.previous_trailing_comments().to_vec();
9274            TableRef {
9275                name: table_name,
9276                schema: None,
9277                catalog: None,
9278                alias: None,
9279                alias_explicit_as: false,
9280                column_aliases: Vec::new(),
9281                when: None,
9282                only: false,
9283                final_: false,
9284                table_sample: None,
9285                hints: Vec::new(),
9286                system_time: None,
9287                trailing_comments,
9288                partitions: Vec::new(),
9289                identifier_func: None,
9290                changes: None,
9291                version: None,
9292                span: None,
9293            }
9294        };
9295
9296        // Optional alias (PostgreSQL: INSERT INTO table AS t(...), Oracle: INSERT INTO table t ...)
9297        let (alias, alias_explicit_as) = if self.match_token(TokenType::As) {
9298            (Some(Identifier::new(self.expect_identifier()?)), true)
9299        } else if self.is_identifier_token()
9300            && !self.check(TokenType::Values)
9301            && !self.check(TokenType::Select)
9302            && !self.check(TokenType::Default)
9303            && !self.check(TokenType::By)
9304            && !self.check(TokenType::Partition)
9305            && !self.check(TokenType::Output)
9306            && !self.check(TokenType::If)
9307            && !self.check(TokenType::Replace)
9308            && !self.check(TokenType::Table)
9309            && !self.check(TokenType::LParen)
9310        {
9311            // Implicit alias without AS (e.g., INSERT INTO dest d VALUES ...)
9312            (Some(Identifier::new(self.expect_identifier()?)), false)
9313        } else {
9314            (None, false)
9315        };
9316
9317        // Optional IF EXISTS (Hive)
9318        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
9319
9320        // Optional REPLACE WHERE clause (Databricks): INSERT INTO a REPLACE WHERE cond VALUES ...
9321        let replace_where =
9322            if self.match_token(TokenType::Replace) && self.match_token(TokenType::Where) {
9323                Some(Box::new(self.parse_or()?))
9324            } else {
9325                None
9326            };
9327
9328        // Optional PARTITION clause
9329        // ClickHouse: PARTITION BY expr (no parens)
9330        // Hive/Spark: PARTITION (col1 = val1, col2)
9331        let mut partition_by_expr: Option<Box<Expression>> = None;
9332        let partition = if self.check(TokenType::Partition) && self.check_next(TokenType::By) {
9333            // ClickHouse PARTITION BY expr
9334            self.skip(); // consume PARTITION
9335            self.skip(); // consume BY
9336            partition_by_expr = Some(Box::new(self.parse_expression()?));
9337            Vec::new()
9338        } else if self.match_token(TokenType::Partition) {
9339            self.expect(TokenType::LParen)?;
9340            let mut parts = Vec::new();
9341            loop {
9342                let col = Identifier::new(self.expect_identifier()?);
9343                let value = if self.match_token(TokenType::Eq) {
9344                    Some(self.parse_expression()?)
9345                } else {
9346                    None
9347                };
9348                parts.push((col, value));
9349                if !self.match_token(TokenType::Comma) {
9350                    break;
9351                }
9352            }
9353            self.expect(TokenType::RParen)?;
9354            parts
9355        } else {
9356            Vec::new()
9357        };
9358
9359        // ClickHouse: SETTINGS key = val, ...
9360        let insert_settings = if self.match_token(TokenType::Settings) {
9361            let mut settings = Vec::new();
9362            loop {
9363                settings.push(self.parse_expression()?);
9364                if !self.match_token(TokenType::Comma) {
9365                    break;
9366                }
9367            }
9368            settings
9369        } else {
9370            Vec::new()
9371        };
9372
9373        // Optional column list OR parenthesized subquery
9374        // We need to check if ( is followed by SELECT/WITH (subquery) or identifiers (column list)
9375        let columns = if self.check(TokenType::LParen) {
9376            // Look ahead to see if this is a subquery or column list
9377            if self
9378                .peek_nth(1)
9379                .map(|t| t.token_type == TokenType::Select || t.token_type == TokenType::With)
9380                .unwrap_or(false)
9381            {
9382                // This is a parenthesized subquery, not a column list
9383                Vec::new()
9384            } else if matches!(
9385                self.config.dialect,
9386                Some(crate::dialects::DialectType::ClickHouse)
9387            ) && {
9388                // ClickHouse: INSERT INTO t (*), t(* EXCEPT ...), t(table.* EXCEPT ...), t(COLUMNS('pattern') EXCEPT ...)
9389                let peek1 = self.peek_nth(1).map(|t| t.token_type);
9390                peek1 == Some(TokenType::Star)
9391                    || (peek1 == Some(TokenType::Var)
9392                        && self.peek_nth(2).map(|t| t.token_type) == Some(TokenType::Dot)
9393                        && self.peek_nth(3).map(|t| t.token_type) == Some(TokenType::Star))
9394                    || (peek1 == Some(TokenType::Var)
9395                        && self
9396                            .peek_nth(1)
9397                            .map(|t| t.text.eq_ignore_ascii_case("COLUMNS"))
9398                            .unwrap_or(false))
9399            } {
9400                // Consume balanced parens and skip entire column specification
9401                self.skip(); // consume (
9402                let mut depth = 1i32;
9403                while !self.is_at_end() && depth > 0 {
9404                    if self.check(TokenType::LParen) {
9405                        depth += 1;
9406                    }
9407                    if self.check(TokenType::RParen) {
9408                        depth -= 1;
9409                        if depth == 0 {
9410                            break;
9411                        }
9412                    }
9413                    self.skip();
9414                }
9415                self.expect(TokenType::RParen)?;
9416                Vec::new() // Treat as "all columns"
9417            } else {
9418                self.skip(); // consume (
9419                let cols = self.parse_identifier_list()?;
9420                self.expect(TokenType::RParen)?;
9421                cols
9422            }
9423        } else {
9424            Vec::new()
9425        };
9426
9427        // Parse OUTPUT clause (TSQL)
9428        let output = if self.match_token(TokenType::Output) {
9429            Some(self.parse_output_clause()?)
9430        } else {
9431            None
9432        };
9433
9434        // Check for BY NAME (DuckDB): INSERT INTO x BY NAME SELECT ...
9435        let by_name = self.match_token(TokenType::By) && self.match_identifier("NAME");
9436
9437        // Check for DEFAULT VALUES (PostgreSQL)
9438        let default_values =
9439            self.match_token(TokenType::Default) && self.match_token(TokenType::Values);
9440
9441        // VALUES or SELECT or TABLE source (Hive/Spark) or DEFAULT VALUES (already consumed above)
9442        let (values, query) = if default_values {
9443            // DEFAULT VALUES: no values or query
9444            (Vec::new(), None)
9445        } else if matches!(
9446            self.config.dialect,
9447            Some(crate::dialects::DialectType::ClickHouse)
9448        ) && self.check(TokenType::Format)
9449            && self.peek_nth(1).is_some_and(|t| {
9450                !t.text.eq_ignore_ascii_case("VALUES")
9451                    && (t.token_type == TokenType::Var || t.token_type == TokenType::Identifier)
9452            })
9453        {
9454            // ClickHouse: FORMAT <format_name> followed by raw data (CSV, JSON, TSV, etc.)
9455            // Skip everything to next semicolon or end — the data is not SQL
9456            self.skip(); // consume FORMAT
9457            let format_name = self.advance().text.clone(); // consume format name
9458                                                           // Consume all remaining tokens until semicolon (raw data)
9459            while !self.is_at_end() && !self.check(TokenType::Semicolon) {
9460                self.skip();
9461            }
9462            // Store as empty values with the format name in the query as a command
9463            (
9464                Vec::new(),
9465                Some(Expression::Command(Box::new(crate::expressions::Command {
9466                    this: format!("FORMAT {}", format_name),
9467                }))),
9468            )
9469        } else if matches!(
9470            self.config.dialect,
9471            Some(crate::dialects::DialectType::ClickHouse)
9472        ) && self.match_text_seq(&["FORMAT", "VALUES"])
9473        {
9474            let mut all_values = Vec::new();
9475
9476            loop {
9477                self.expect(TokenType::LParen)?;
9478                let row = self.parse_expression_list()?;
9479                self.expect(TokenType::RParen)?;
9480                all_values.push(row);
9481
9482                if !self.match_token(TokenType::Comma) {
9483                    break;
9484                }
9485            }
9486
9487            (all_values, None)
9488        } else if self.match_token(TokenType::Values) {
9489            let mut all_values = Vec::new();
9490
9491            // ClickHouse: INSERT INTO t VALUES; — empty VALUES (clientError expected)
9492            if matches!(
9493                self.config.dialect,
9494                Some(crate::dialects::DialectType::ClickHouse)
9495            ) && (self.check(TokenType::Semicolon) || self.is_at_end())
9496            {
9497                // Return empty INSERT as Command to avoid needing all Insert fields
9498                return Ok(Expression::Command(Box::new(crate::expressions::Command {
9499                    this: "INSERT INTO VALUES".to_string(),
9500                })));
9501            }
9502
9503            // ClickHouse: allow bare VALUES without parens: VALUES 1, 2, 3
9504            if matches!(
9505                self.config.dialect,
9506                Some(crate::dialects::DialectType::ClickHouse)
9507            ) && !self.check(TokenType::LParen)
9508            {
9509                loop {
9510                    let val = self.parse_expression()?;
9511                    all_values.push(vec![val]);
9512                    if !self.match_token(TokenType::Comma) {
9513                        break;
9514                    }
9515                }
9516            } else {
9517                loop {
9518                    self.expect(TokenType::LParen)?;
9519                    // ClickHouse: allow empty VALUES () — empty tuple
9520                    let row = if self.check(TokenType::RParen) {
9521                        Vec::new()
9522                    } else {
9523                        self.parse_values_expression_list()?
9524                    };
9525                    self.expect(TokenType::RParen)?;
9526                    all_values.push(row);
9527
9528                    if !self.match_token(TokenType::Comma) {
9529                        // ClickHouse: allow tuples without commas: VALUES (1) (2) (3)
9530                        if matches!(
9531                            self.config.dialect,
9532                            Some(crate::dialects::DialectType::ClickHouse)
9533                        ) && self.check(TokenType::LParen)
9534                        {
9535                            continue;
9536                        }
9537                        break;
9538                    }
9539                    // ClickHouse: allow trailing comma after last tuple
9540                    if matches!(
9541                        self.config.dialect,
9542                        Some(crate::dialects::DialectType::ClickHouse)
9543                    ) && !self.check(TokenType::LParen)
9544                    {
9545                        break;
9546                    }
9547                }
9548            } // close else (parenthesized values)
9549
9550            (all_values, None)
9551        } else if self.check(TokenType::Table) {
9552            // Hive/Spark: INSERT OVERWRITE TABLE target TABLE source
9553            // The TABLE keyword here indicates source table, not a subquery
9554            (Vec::new(), None)
9555        } else {
9556            (Vec::new(), Some(self.parse_statement()?))
9557        };
9558
9559        // Parse source table (Hive/Spark): INSERT OVERWRITE TABLE target TABLE source
9560        let source = if self.match_token(TokenType::Table) {
9561            // Parse source table reference (similar to target table parsing)
9562            let source_name = self.expect_identifier_with_quoted()?;
9563            let source_table = if self.match_token(TokenType::Dot) {
9564                let schema = source_name;
9565                let name = self.expect_identifier_with_quoted()?;
9566                let trailing_comments = self.previous_trailing_comments().to_vec();
9567                TableRef {
9568                    name,
9569                    schema: Some(schema),
9570                    catalog: None,
9571                    alias: None,
9572                    alias_explicit_as: false,
9573                    column_aliases: Vec::new(),
9574                    trailing_comments,
9575                    when: None,
9576                    only: false,
9577                    final_: false,
9578                    table_sample: None,
9579                    hints: Vec::new(),
9580                    system_time: None,
9581                    partitions: Vec::new(),
9582                    identifier_func: None,
9583                    changes: None,
9584                    version: None,
9585                    span: None,
9586                }
9587            } else {
9588                let trailing_comments = self.previous_trailing_comments().to_vec();
9589                TableRef {
9590                    name: source_name,
9591                    schema: None,
9592                    catalog: None,
9593                    alias: None,
9594                    alias_explicit_as: false,
9595                    column_aliases: Vec::new(),
9596                    trailing_comments,
9597                    when: None,
9598                    only: false,
9599                    final_: false,
9600                    table_sample: None,
9601                    hints: Vec::new(),
9602                    system_time: None,
9603                    partitions: Vec::new(),
9604                    identifier_func: None,
9605                    changes: None,
9606                    version: None,
9607                    span: None,
9608                }
9609            };
9610            Some(Expression::Table(Box::new(source_table)))
9611        } else {
9612            None
9613        };
9614
9615        // Parse optional AS alias after VALUES (MySQL: INSERT ... VALUES (...) AS new_data)
9616        let source_alias = if self.match_token(TokenType::As) {
9617            Some(Identifier::new(self.expect_identifier()?))
9618        } else {
9619            None
9620        };
9621
9622        // Parse ON CONFLICT clause (PostgreSQL, SQLite) or ON DUPLICATE KEY UPDATE (MySQL)
9623        let on_conflict = if self.match_token(TokenType::On) {
9624            if self.match_identifier("CONFLICT") {
9625                Some(Box::new(self.parse_on_conflict()?))
9626            } else if self.match_identifier("DUPLICATE") {
9627                // MySQL: ON DUPLICATE KEY UPDATE
9628                self.expect(TokenType::Key)?;
9629                self.expect(TokenType::Update)?;
9630
9631                // Parse the UPDATE SET expressions
9632                let mut sets = Vec::new();
9633                loop {
9634                    // Parse column = expression
9635                    let col_name = self.expect_identifier_with_quoted()?;
9636                    // Handle qualified column: table.column
9637                    let column = if self.match_token(TokenType::Dot) {
9638                        let col = self.expect_identifier_with_quoted()?;
9639                        Expression::boxed_column(Column {
9640                            name: col,
9641                            table: Some(col_name),
9642                            join_mark: false,
9643                            trailing_comments: Vec::new(),
9644                            span: None,
9645                            inferred_type: None,
9646                        })
9647                    } else {
9648                        Expression::Identifier(col_name)
9649                    };
9650                    self.expect(TokenType::Eq)?;
9651                    let value = self.parse_expression()?;
9652                    sets.push(Expression::Eq(Box::new(BinaryOp {
9653                        left: column,
9654                        right: value,
9655                        left_comments: Vec::new(),
9656                        operator_comments: Vec::new(),
9657                        trailing_comments: Vec::new(),
9658                        inferred_type: None,
9659                    })));
9660                    if !self.match_token(TokenType::Comma) {
9661                        break;
9662                    }
9663                }
9664
9665                Some(Box::new(Expression::OnConflict(Box::new(OnConflict {
9666                    duplicate: Some(Box::new(Expression::Boolean(BooleanLiteral {
9667                        value: true,
9668                    }))),
9669                    expressions: sets,
9670                    action: None,
9671                    conflict_keys: None,
9672                    index_predicate: None,
9673                    constraint: None,
9674                    where_: None,
9675                }))))
9676            } else {
9677                // Unexpected token after ON
9678                return Err(self.parse_error("Expected CONFLICT or DUPLICATE after ON"));
9679            }
9680        } else {
9681            None
9682        };
9683
9684        // Parse RETURNING clause (PostgreSQL, SQLite)
9685        let returning = if self.match_token(TokenType::Returning) {
9686            self.parse_select_expressions()?
9687        } else {
9688            Vec::new()
9689        };
9690
9691        Ok(Expression::Insert(Box::new(Insert {
9692            table,
9693            columns,
9694            values,
9695            query,
9696            overwrite,
9697            partition,
9698            directory: None,
9699            returning,
9700            output,
9701            on_conflict,
9702            leading_comments,
9703            if_exists,
9704            with: None,
9705            ignore,
9706            source_alias,
9707            alias,
9708            alias_explicit_as,
9709            default_values,
9710            by_name,
9711            conflict_action,
9712            is_replace: false,
9713            replace_where,
9714            source: source.map(Box::new),
9715            hint,
9716            function_target,
9717            partition_by: partition_by_expr,
9718            settings: insert_settings,
9719        })))
9720    }
9721
9722    /// Parse ON CONFLICT clause for INSERT statements (PostgreSQL, SQLite)
9723    /// Syntax: ON CONFLICT [(conflict_target)] [WHERE predicate] DO NOTHING | DO UPDATE SET ...
9724    /// ON CONFLICT ON CONSTRAINT constraint_name DO ...
9725    fn parse_on_conflict(&mut self) -> Result<Expression> {
9726        // Check for ON CONSTRAINT variant
9727        let constraint =
9728            if self.match_token(TokenType::On) && self.match_token(TokenType::Constraint) {
9729                let name = self.expect_identifier()?;
9730                Some(Box::new(Expression::Identifier(Identifier::new(name))))
9731            } else {
9732                None
9733            };
9734
9735        // Parse optional conflict target (column list)
9736        let conflict_keys = if constraint.is_none() && self.match_token(TokenType::LParen) {
9737            let keys = self.parse_expression_list()?;
9738            self.expect(TokenType::RParen)?;
9739            Some(Box::new(Expression::Tuple(Box::new(Tuple {
9740                expressions: keys,
9741            }))))
9742        } else {
9743            None
9744        };
9745
9746        // Parse optional WHERE clause for conflict target
9747        let index_predicate = if self.match_token(TokenType::Where) {
9748            Some(Box::new(self.parse_expression()?))
9749        } else {
9750            None
9751        };
9752
9753        // Parse DO NOTHING or DO UPDATE
9754        if !self.match_identifier("DO") {
9755            return Err(self.parse_error("Expected DO after ON CONFLICT"));
9756        }
9757
9758        let action = if self.match_identifier("NOTHING") {
9759            // DO NOTHING
9760            Some(Box::new(Expression::Identifier(Identifier::new(
9761                "NOTHING".to_string(),
9762            ))))
9763        } else if self.match_token(TokenType::Update) {
9764            // DO UPDATE SET ...
9765            self.expect(TokenType::Set)?;
9766            let mut sets = Vec::new();
9767            loop {
9768                // Parse column = expression
9769                let col_name = self.expect_identifier_with_quoted()?;
9770                // Handle qualified column: table.column
9771                let column = if self.match_token(TokenType::Dot) {
9772                    let col = self.expect_identifier_with_quoted()?;
9773                    Expression::boxed_column(Column {
9774                        name: col,
9775                        table: Some(col_name),
9776                        join_mark: false,
9777                        trailing_comments: Vec::new(),
9778                        span: None,
9779                        inferred_type: None,
9780                    })
9781                } else {
9782                    Expression::Identifier(col_name)
9783                };
9784                self.expect(TokenType::Eq)?;
9785                let value = self.parse_expression()?;
9786                sets.push(Expression::Eq(Box::new(BinaryOp {
9787                    left: column,
9788                    right: value,
9789                    left_comments: Vec::new(),
9790                    operator_comments: Vec::new(),
9791                    trailing_comments: Vec::new(),
9792                    inferred_type: None,
9793                })));
9794                if !self.match_token(TokenType::Comma) {
9795                    break;
9796                }
9797            }
9798            Some(Box::new(Expression::Tuple(Box::new(Tuple {
9799                expressions: sets,
9800            }))))
9801        } else {
9802            return Err(self.parse_error("Expected NOTHING or UPDATE after DO"));
9803        };
9804
9805        // Parse optional WHERE clause for the UPDATE action
9806        let where_ = if self.match_token(TokenType::Where) {
9807            Some(Box::new(self.parse_expression()?))
9808        } else {
9809            None
9810        };
9811
9812        Ok(Expression::OnConflict(Box::new(OnConflict {
9813            duplicate: None,
9814            expressions: Vec::new(),
9815            action,
9816            conflict_keys,
9817            index_predicate,
9818            constraint,
9819            where_,
9820        })))
9821    }
9822
9823    /// Parse MySQL REPLACE [INTO] statement or REPLACE() function call
9824    fn parse_replace(&mut self) -> Result<Expression> {
9825        // Check if this is REPLACE() function call (REPLACE followed by '(')
9826        // or MySQL REPLACE INTO statement
9827        let replace_token = self.expect(TokenType::Replace)?;
9828        let leading_comments = replace_token.comments;
9829
9830        if self.check(TokenType::LParen) {
9831            // This is a REPLACE() function call, parse as expression
9832            self.expect(TokenType::LParen)?;
9833            let args = self.parse_expression_list()?;
9834            self.expect(TokenType::RParen)?;
9835            return Ok(Expression::Function(Box::new(Function {
9836                name: "REPLACE".to_string(),
9837                args,
9838                distinct: false,
9839                trailing_comments: Vec::new(),
9840                use_bracket_syntax: false,
9841                no_parens: false,
9842                quoted: false,
9843                span: None,
9844                inferred_type: None,
9845            })));
9846        }
9847
9848        // Teradata: REPLACE VIEW -> CREATE OR REPLACE VIEW
9849        if matches!(
9850            self.config.dialect,
9851            Some(crate::dialects::DialectType::Teradata)
9852        ) && self.check(TokenType::View)
9853        {
9854            return self.parse_create_view(true, false, false, None, None, None, false);
9855        }
9856
9857        // ClickHouse: REPLACE TABLE -> treat like CREATE OR REPLACE TABLE
9858        // Also handle REPLACE TEMPORARY TABLE
9859        if matches!(
9860            self.config.dialect,
9861            Some(crate::dialects::DialectType::ClickHouse)
9862        ) && (self.check(TokenType::Table) || self.check(TokenType::Temporary))
9863        {
9864            let temporary = self.match_token(TokenType::Temporary);
9865            return self.parse_create_table(true, temporary, leading_comments.clone(), None);
9866        }
9867
9868        // ClickHouse: REPLACE DICTIONARY -> consume as Command
9869        if matches!(
9870            self.config.dialect,
9871            Some(crate::dialects::DialectType::ClickHouse)
9872        ) && (self.check(TokenType::Dictionary) || self.check_identifier("DICTIONARY"))
9873        {
9874            let mut parts = vec!["REPLACE".to_string()];
9875            let mut _paren_depth = 0i32;
9876            while !self.is_at_end() && !self.check(TokenType::Semicolon) {
9877                let token = self.advance();
9878                if token.token_type == TokenType::LParen {
9879                    _paren_depth += 1;
9880                }
9881                if token.token_type == TokenType::RParen {
9882                    _paren_depth -= 1;
9883                }
9884                let text = if token.token_type == TokenType::String {
9885                    format!("'{}'", token.text)
9886                } else if token.token_type == TokenType::QuotedIdentifier {
9887                    format!("\"{}\"", token.text)
9888                } else {
9889                    token.text.clone()
9890                };
9891                parts.push(text);
9892            }
9893            return Ok(Expression::Command(Box::new(crate::expressions::Command {
9894                this: parts.join(" "),
9895            })));
9896        }
9897
9898        // Otherwise, this is MySQL/SQLite REPLACE INTO statement - parse similarly to INSERT
9899        self.match_token(TokenType::Into);
9900
9901        let table_name = self.expect_identifier_or_safe_keyword_with_quoted()?;
9902        let table = if self.match_token(TokenType::Dot) {
9903            let second_name = self.expect_identifier_or_safe_keyword_with_quoted()?;
9904            TableRef {
9905                name: second_name,
9906                schema: Some(table_name),
9907                catalog: None,
9908                alias: None,
9909                alias_explicit_as: false,
9910                column_aliases: Vec::new(),
9911                trailing_comments: Vec::new(),
9912                when: None,
9913                only: false,
9914                final_: false,
9915                table_sample: None,
9916                hints: Vec::new(),
9917                system_time: None,
9918                partitions: Vec::new(),
9919                identifier_func: None,
9920                changes: None,
9921                version: None,
9922                span: None,
9923            }
9924        } else {
9925            TableRef::new(table_name.name)
9926        };
9927
9928        // Parse optional column list
9929        let columns = if self.match_token(TokenType::LParen) {
9930            let mut cols = Vec::new();
9931            loop {
9932                if self.check(TokenType::RParen) {
9933                    break;
9934                }
9935                let col = self.expect_identifier_with_quoted()?;
9936                cols.push(col);
9937                if !self.match_token(TokenType::Comma) {
9938                    break;
9939                }
9940            }
9941            self.expect(TokenType::RParen)?;
9942            cols
9943        } else {
9944            Vec::new()
9945        };
9946
9947        // Parse VALUES or SELECT query
9948        let mut values = Vec::new();
9949        let query = if self.match_token(TokenType::Values) {
9950            loop {
9951                self.expect(TokenType::LParen)?;
9952                let row = self.parse_expression_list()?;
9953                self.expect(TokenType::RParen)?;
9954                values.push(row);
9955                if !self.match_token(TokenType::Comma) {
9956                    break;
9957                }
9958            }
9959            None
9960        } else if !self.is_at_end() && !self.check(TokenType::Semicolon) {
9961            // SELECT or other statement as value source
9962            Some(self.parse_statement()?)
9963        } else {
9964            None
9965        };
9966
9967        Ok(Expression::Insert(Box::new(Insert {
9968            table,
9969            columns,
9970            values,
9971            query,
9972            overwrite: false,
9973            partition: Vec::new(),
9974            directory: None,
9975            returning: Vec::new(),
9976            output: None,
9977            on_conflict: None,
9978            leading_comments,
9979            if_exists: false,
9980            with: None,
9981            ignore: false,
9982            source_alias: None,
9983            alias: None,
9984            alias_explicit_as: false,
9985            default_values: false,
9986            by_name: false,
9987            conflict_action: None,
9988            is_replace: true,
9989            replace_where: None,
9990            source: None,
9991            hint: None,
9992            function_target: None,
9993            partition_by: None,
9994            settings: Vec::new(),
9995        })))
9996    }
9997
9998    /// Parse UPDATE statement
9999    fn parse_update(&mut self) -> Result<Expression> {
10000        let update_token = self.expect(TokenType::Update)?;
10001        let leading_comments = update_token.comments;
10002
10003        // TSQL: UPDATE STATISTICS table_name - parse as Command
10004        if self.check_identifier("STATISTICS") {
10005            let mut parts = vec!["UPDATE".to_string()];
10006            while !self.is_at_end() && !self.check(TokenType::Semicolon) {
10007                parts.push(self.advance().text);
10008            }
10009            return Ok(Expression::Command(Box::new(Command {
10010                this: parts.join(" "),
10011            })));
10012        }
10013
10014        // Parse table name (can be qualified: db.table_name)
10015        let first_name = self.expect_identifier_with_quoted()?;
10016        let mut table = if self.match_token(TokenType::Dot) {
10017            let second_name = self.expect_identifier_with_quoted()?;
10018            // Check for three-part name (catalog.schema.table)
10019            if self.match_token(TokenType::Dot) {
10020                let table_name = self.expect_identifier_with_quoted()?;
10021                TableRef {
10022                    name: table_name,
10023                    schema: Some(second_name),
10024                    catalog: Some(first_name),
10025                    alias: None,
10026                    alias_explicit_as: false,
10027                    column_aliases: Vec::new(),
10028                    trailing_comments: Vec::new(),
10029                    when: None,
10030                    only: false,
10031                    final_: false,
10032                    table_sample: None,
10033                    hints: Vec::new(),
10034                    system_time: None,
10035                    partitions: Vec::new(),
10036                    identifier_func: None,
10037                    changes: None,
10038                    version: None,
10039                    span: None,
10040                }
10041            } else {
10042                TableRef {
10043                    name: second_name,
10044                    schema: Some(first_name),
10045                    catalog: None,
10046                    alias: None,
10047                    alias_explicit_as: false,
10048                    column_aliases: Vec::new(),
10049                    trailing_comments: Vec::new(),
10050                    when: None,
10051                    only: false,
10052                    final_: false,
10053                    table_sample: None,
10054                    hints: Vec::new(),
10055                    system_time: None,
10056                    partitions: Vec::new(),
10057                    identifier_func: None,
10058                    changes: None,
10059                    version: None,
10060                    span: None,
10061                }
10062            }
10063        } else {
10064            TableRef::from_identifier(first_name)
10065        };
10066        table.trailing_comments = self.previous_trailing_comments().to_vec();
10067
10068        // Optional alias (with or without AS)
10069        if self.match_token(TokenType::As) {
10070            table.alias = Some(self.expect_identifier_with_quoted()?);
10071            table.alias_explicit_as = true;
10072        } else if self.is_identifier_token() && !self.check(TokenType::Set) {
10073            // Implicit alias (table t SET ...)
10074            table.alias = Some(self.expect_identifier_with_quoted()?);
10075            table.alias_explicit_as = false;
10076        }
10077
10078        // Handle multi-table UPDATE syntax: UPDATE t1, t2, t3 LEFT JOIN t4 ON ... SET ...
10079        // Capture additional tables
10080        let mut extra_tables = Vec::new();
10081        while self.match_token(TokenType::Comma) {
10082            // Parse additional table name
10083            let first_name = self.expect_identifier_with_quoted()?;
10084            let mut extra_table = if self.match_token(TokenType::Dot) {
10085                let second_name = self.expect_identifier_with_quoted()?;
10086                if self.match_token(TokenType::Dot) {
10087                    let table_name = self.expect_identifier_with_quoted()?;
10088                    TableRef {
10089                        name: table_name,
10090                        schema: Some(second_name),
10091                        catalog: Some(first_name),
10092                        alias: None,
10093                        alias_explicit_as: false,
10094                        column_aliases: Vec::new(),
10095                        trailing_comments: Vec::new(),
10096                        when: None,
10097                        only: false,
10098                        final_: false,
10099                        table_sample: None,
10100                        hints: Vec::new(),
10101                        system_time: None,
10102                        partitions: Vec::new(),
10103                        identifier_func: None,
10104                        changes: None,
10105                        version: None,
10106                        span: None,
10107                    }
10108                } else {
10109                    TableRef {
10110                        name: second_name,
10111                        schema: Some(first_name),
10112                        catalog: None,
10113                        alias: None,
10114                        alias_explicit_as: false,
10115                        column_aliases: Vec::new(),
10116                        trailing_comments: Vec::new(),
10117                        when: None,
10118                        only: false,
10119                        final_: false,
10120                        table_sample: None,
10121                        hints: Vec::new(),
10122                        system_time: None,
10123                        partitions: Vec::new(),
10124                        identifier_func: None,
10125                        changes: None,
10126                        version: None,
10127                        span: None,
10128                    }
10129                }
10130            } else {
10131                TableRef::from_identifier(first_name)
10132            };
10133            // Optional alias
10134            if self.match_token(TokenType::As) {
10135                extra_table.alias = Some(self.expect_identifier_with_quoted()?);
10136                extra_table.alias_explicit_as = true;
10137            } else if self.is_identifier_token()
10138                && !self.check(TokenType::Set)
10139                && !self.check_keyword()
10140            {
10141                extra_table.alias = Some(self.expect_identifier_with_quoted()?);
10142                extra_table.alias_explicit_as = false;
10143            }
10144            extra_tables.push(extra_table);
10145        }
10146
10147        // Handle JOINs before SET
10148        let mut table_joins = Vec::new();
10149        while let Some((kind, _, use_inner_keyword, use_outer_keyword, _join_hint)) =
10150            self.try_parse_join_kind()
10151        {
10152            if self.check(TokenType::Join) {
10153                self.skip(); // consume JOIN
10154            }
10155            // Parse joined table
10156            let first_name = self.expect_identifier_with_quoted()?;
10157            let mut join_table = if self.match_token(TokenType::Dot) {
10158                let second_name = self.expect_identifier_with_quoted()?;
10159                if self.match_token(TokenType::Dot) {
10160                    let table_name = self.expect_identifier_with_quoted()?;
10161                    TableRef {
10162                        name: table_name,
10163                        schema: Some(second_name),
10164                        catalog: Some(first_name),
10165                        alias: None,
10166                        alias_explicit_as: false,
10167                        column_aliases: Vec::new(),
10168                        trailing_comments: Vec::new(),
10169                        when: None,
10170                        only: false,
10171                        final_: false,
10172                        table_sample: None,
10173                        hints: Vec::new(),
10174                        system_time: None,
10175                        partitions: Vec::new(),
10176                        identifier_func: None,
10177                        changes: None,
10178                        version: None,
10179                        span: None,
10180                    }
10181                } else {
10182                    TableRef {
10183                        name: second_name,
10184                        schema: Some(first_name),
10185                        catalog: None,
10186                        alias: None,
10187                        alias_explicit_as: false,
10188                        column_aliases: Vec::new(),
10189                        trailing_comments: Vec::new(),
10190                        when: None,
10191                        only: false,
10192                        final_: false,
10193                        table_sample: None,
10194                        hints: Vec::new(),
10195                        system_time: None,
10196                        partitions: Vec::new(),
10197                        changes: None,
10198                        version: None,
10199                        identifier_func: None,
10200                        span: None,
10201                    }
10202                }
10203            } else {
10204                TableRef::from_identifier(first_name)
10205            };
10206            // Optional alias
10207            if self.match_token(TokenType::As) {
10208                join_table.alias = Some(self.expect_identifier_with_quoted()?);
10209                join_table.alias_explicit_as = true;
10210            } else if self.is_identifier_token()
10211                && !self.check(TokenType::On)
10212                && !self.check(TokenType::Set)
10213            {
10214                join_table.alias = Some(self.expect_identifier_with_quoted()?);
10215                join_table.alias_explicit_as = false;
10216            }
10217            // ON clause
10218            let on_condition = if self.match_token(TokenType::On) {
10219                Some(self.parse_expression()?)
10220            } else {
10221                None
10222            };
10223            table_joins.push(Join {
10224                this: Expression::Table(Box::new(join_table)),
10225                on: on_condition,
10226                using: Vec::new(),
10227                kind,
10228                use_inner_keyword,
10229                use_outer_keyword,
10230                deferred_condition: false,
10231                join_hint: None,
10232                match_condition: None,
10233                pivots: Vec::new(),
10234                comments: Vec::new(),
10235                nesting_group: 0,
10236                directed: false,
10237            });
10238        }
10239
10240        // Snowflake syntax: UPDATE table FROM (source) SET ... WHERE ...
10241        // Check if FROM comes before SET
10242        let (from_before_set, early_from_clause, early_from_joins) =
10243            if self.match_token(TokenType::From) {
10244                let from_clause = self.parse_from()?;
10245                let from_joins = self.parse_joins()?;
10246                (true, Some(from_clause), from_joins)
10247            } else {
10248                (false, None, Vec::new())
10249            };
10250
10251        self.expect(TokenType::Set)?;
10252
10253        let mut set = Vec::new();
10254        loop {
10255            // Column can be qualified for multi-table UPDATE (e.g., a.id = 1)
10256            // Use safe keyword variant to allow keywords like 'exists' as column names (ClickHouse)
10257            let mut col_ident = self.expect_identifier_or_safe_keyword_with_quoted()?;
10258            while self.match_token(TokenType::Dot) {
10259                let part = self.expect_identifier_or_safe_keyword_with_quoted()?;
10260                // For qualified columns, preserve both parts
10261                col_ident = Identifier {
10262                    name: format!("{}.{}", col_ident.name, part.name),
10263                    quoted: col_ident.quoted || part.quoted,
10264                    trailing_comments: Vec::new(),
10265                    span: None,
10266                };
10267            }
10268            self.expect(TokenType::Eq)?;
10269            let value = self.parse_expression()?;
10270            set.push((col_ident, value));
10271
10272            if !self.match_token(TokenType::Comma) {
10273                break;
10274            }
10275        }
10276
10277        // Parse OUTPUT clause (TSQL)
10278        let output = if self.match_token(TokenType::Output) {
10279            Some(self.parse_output_clause()?)
10280        } else {
10281            None
10282        };
10283
10284        // Parse FROM clause (PostgreSQL, SQL Server, Snowflake) - only if not already parsed before SET
10285        let (from_clause, from_joins) = if from_before_set {
10286            (early_from_clause, early_from_joins)
10287        } else if self.match_token(TokenType::From) {
10288            let from_clause = Some(self.parse_from()?);
10289            let from_joins = self.parse_joins()?;
10290            (from_clause, from_joins)
10291        } else {
10292            (None, Vec::new())
10293        };
10294
10295        let where_clause = if self.match_token(TokenType::Where) {
10296            Some(Where {
10297                this: self.parse_expression()?,
10298            })
10299        } else {
10300            None
10301        };
10302
10303        // Parse RETURNING clause (PostgreSQL, SQLite)
10304        let returning = if self.match_token(TokenType::Returning) {
10305            self.parse_select_expressions()?
10306        } else {
10307            Vec::new()
10308        };
10309
10310        // Parse ORDER BY clause (MySQL)
10311        let order_by = if self.match_keywords(&[TokenType::Order, TokenType::By]) {
10312            Some(self.parse_order_by()?)
10313        } else {
10314            None
10315        };
10316
10317        // Parse LIMIT clause (MySQL)
10318        let limit = if self.match_token(TokenType::Limit) {
10319            Some(self.parse_expression()?)
10320        } else {
10321            None
10322        };
10323
10324        Ok(Expression::Update(Box::new(Update {
10325            table,
10326            extra_tables,
10327            table_joins,
10328            set,
10329            from_clause,
10330            from_joins,
10331            where_clause,
10332            returning,
10333            output,
10334            with: None,
10335            leading_comments,
10336            limit,
10337            order_by,
10338            from_before_set,
10339        })))
10340    }
10341
10342    /// Parse DELETE statement
10343    /// Handles:
10344    /// - Standard: DELETE FROM t WHERE ...
10345    /// - PostgreSQL USING: DELETE FROM t USING s WHERE ... RETURNING a
10346    /// - DuckDB USING: DELETE FROM t USING (VALUES ...) AS t1 WHERE ...
10347    /// - MySQL multi-table: DELETE t1 FROM t1 JOIN t2 ON ... WHERE ...
10348    /// - MySQL multi-table: DELETE t1, t2 FROM t1 JOIN t2 JOIN t3 WHERE ...
10349    /// - MySQL USING: DELETE FROM t1, t2 USING t1 JOIN t2 JOIN t3 WHERE ...
10350    /// - MySQL FORCE INDEX: DELETE FROM t FORCE INDEX (idx) WHERE ...
10351    fn parse_delete(&mut self) -> Result<Expression> {
10352        let delete_token = self.expect(TokenType::Delete)?;
10353        let leading_comments = delete_token.comments;
10354
10355        // Check if FROM is present. If not, this is MySQL multi-table: DELETE t1, t2 FROM ...
10356        // or TSQL: DELETE x OUTPUT x.a FROM z
10357        let mut tables = Vec::new();
10358        let mut early_output = None;
10359        let _has_from = if self.check(TokenType::From) {
10360            self.skip(); // consume FROM
10361            true
10362        } else {
10363            // MySQL multi-table: DELETE t1[, t2, ...] FROM ...
10364            // or TSQL: DELETE x OUTPUT x.a FROM z
10365            // or BigQuery/generic: DELETE table WHERE ... (no FROM required)
10366            // Parse target table list (supporting dotted names)
10367            loop {
10368                let tref = self.parse_table_ref()?;
10369                tables.push(tref);
10370                if !self.match_token(TokenType::Comma) {
10371                    break;
10372                }
10373            }
10374            // TSQL: OUTPUT clause can appear before FROM
10375            if self.match_token(TokenType::Output) {
10376                early_output = Some(self.parse_output_clause()?);
10377            }
10378            if self.check(TokenType::From) {
10379                self.skip(); // consume FROM
10380                true
10381            } else {
10382                // BigQuery-style: DELETE table WHERE ... (no FROM)
10383                false
10384            }
10385        };
10386
10387        // Now parse the main table after FROM (or use from no-FROM path)
10388        let table = if _has_from {
10389            // Parse the main table(s) after FROM
10390            // Use parse_table_ref() to handle dotted names like db.table
10391            self.parse_table_ref()?
10392        } else {
10393            // BigQuery-style: table was already parsed into `tables`
10394            // Move it out to be the main table
10395            if !tables.is_empty() {
10396                tables.remove(0)
10397            } else {
10398                return Err(self.parse_error("Expected table name in DELETE statement"));
10399            }
10400        };
10401
10402        // ClickHouse: ON CLUSTER clause
10403        let on_cluster = self.parse_on_cluster_clause()?;
10404
10405        // Check for additional tables after the first: DELETE FROM t1, t2 USING ...
10406        let mut extra_from_tables = Vec::new();
10407        if _has_from
10408            && tables.is_empty()
10409            && self.check(TokenType::Comma)
10410            && !self.check(TokenType::Where)
10411        {
10412            // Could be multi-table: DELETE FROM t1, t2 USING ...
10413            // Check ahead if this is followed by USING or more tables
10414            while self.match_token(TokenType::Comma) {
10415                let extra_name = self.expect_identifier_with_quoted()?;
10416                let extra_ref = TableRef::from_identifier(extra_name);
10417                extra_from_tables.push(extra_ref);
10418            }
10419        }
10420
10421        // If we had DELETE FROM t1, t2 USING ..., the tables field stores t1, t2
10422        let mut tables_from_using = false;
10423        if !extra_from_tables.is_empty() {
10424            // The main table + extra tables form the multi-table target
10425            tables.push(table.clone());
10426            tables.append(&mut extra_from_tables);
10427            tables_from_using = true;
10428        }
10429
10430        // Check for FORCE INDEX hint (MySQL): DELETE FROM t FORCE INDEX (idx)
10431        let force_index = if self.match_text_seq(&["FORCE", "INDEX"]) {
10432            self.expect(TokenType::LParen)?;
10433            let idx_name = self.expect_identifier_with_quoted()?;
10434            self.expect(TokenType::RParen)?;
10435            Some(idx_name.name)
10436        } else {
10437            None
10438        };
10439
10440        // Check for optional alias (with or without AS)
10441        let (alias, alias_explicit_as) = if force_index.is_none() && self.match_token(TokenType::As)
10442        {
10443            (Some(self.expect_identifier_with_quoted()?), true)
10444        } else if force_index.is_none()
10445            && self.is_identifier_token()
10446            && !self.check(TokenType::Using)
10447            && !self.check(TokenType::Where)
10448            && !self.check(TokenType::Inner)
10449            && !self.check(TokenType::Left)
10450            && !self.check(TokenType::Right)
10451            && !self.check(TokenType::Cross)
10452            && !self.check(TokenType::Full)
10453            && !self.check(TokenType::Join)
10454            && !self.check_identifier("FORCE")
10455        {
10456            (Some(self.expect_identifier_with_quoted()?), false)
10457        } else {
10458            (None, false)
10459        };
10460
10461        // Parse JOINs for MySQL multi-table: DELETE t1 FROM t1 LEFT JOIN t2 ON ...
10462        let mut joins = self.parse_joins()?;
10463
10464        // Parse USING clause (PostgreSQL/DuckDB/MySQL)
10465        let mut using = Vec::new();
10466        if self.match_token(TokenType::Using) {
10467            loop {
10468                // Check for subquery: USING (SELECT ...) AS ... or (VALUES ...) AS ...
10469                if self.check(TokenType::LParen) {
10470                    // Check if next token after ( is VALUES
10471                    let is_values = self.current + 1 < self.tokens.len()
10472                        && self.tokens[self.current + 1].token_type == TokenType::Values;
10473                    let subquery = if is_values {
10474                        // Parse (VALUES ...) as parenthesized VALUES
10475                        self.skip(); // consume (
10476                        let values = self.parse_values()?;
10477                        self.expect(TokenType::RParen)?;
10478                        Expression::Paren(Box::new(Paren {
10479                            this: values,
10480                            trailing_comments: Vec::new(),
10481                        }))
10482                    } else {
10483                        // Parse as subquery (SELECT ...) or other expression
10484                        self.parse_primary()?
10485                    };
10486                    // Parse alias
10487                    let using_alias = if self.match_token(TokenType::As) {
10488                        let alias_name = self.expect_identifier_with_quoted()?;
10489                        // Check for column aliases: AS name(col1, col2)
10490                        let col_aliases = if self.match_token(TokenType::LParen) {
10491                            let aliases = self.parse_identifier_list()?;
10492                            self.expect(TokenType::RParen)?;
10493                            aliases
10494                        } else {
10495                            Vec::new()
10496                        };
10497                        Some((alias_name, col_aliases))
10498                    } else {
10499                        None
10500                    };
10501                    // Create a TableRef from the subquery with alias
10502                    let mut tref = TableRef::new("");
10503                    if let Some((alias_name, col_aliases)) = using_alias {
10504                        tref.alias = Some(alias_name);
10505                        tref.alias_explicit_as = true;
10506                        tref.column_aliases = col_aliases;
10507                    }
10508                    // Store the subquery in the table reference using hints (as a hack)
10509                    // Actually, we need a better approach - use the table ref hints to store the subquery
10510                    tref.hints = vec![subquery];
10511                    using.push(tref);
10512                } else {
10513                    let using_table = self.expect_identifier_with_quoted()?;
10514                    let mut using_ref = TableRef::from_identifier(using_table);
10515
10516                    // Check for JOINs: USING t1 INNER JOIN t2 INNER JOIN t3
10517                    if self.check_join_keyword() {
10518                        // Parse JOINs as part of USING
10519                        using.push(using_ref);
10520                        let mut using_joins = self.parse_joins()?;
10521                        joins.append(&mut using_joins);
10522                        break;
10523                    }
10524
10525                    // Optional alias for using table
10526                    if self.match_token(TokenType::As) {
10527                        using_ref.alias = Some(self.expect_identifier_with_quoted()?);
10528                        using_ref.alias_explicit_as = true;
10529                    } else if self.is_identifier_token()
10530                        && !self.check(TokenType::Comma)
10531                        && !self.check(TokenType::Where)
10532                    {
10533                        using_ref.alias = Some(self.expect_identifier_with_quoted()?);
10534                    }
10535                    using.push(using_ref);
10536                }
10537                if !self.match_token(TokenType::Comma) {
10538                    break;
10539                }
10540            }
10541        }
10542
10543        // ClickHouse: IN PARTITION 'partition_id' clause before WHERE
10544        if matches!(
10545            self.config.dialect,
10546            Some(crate::dialects::DialectType::ClickHouse)
10547        ) && self.check(TokenType::In)
10548            && self
10549                .peek_nth(1)
10550                .is_some_and(|t| t.text.eq_ignore_ascii_case("PARTITION"))
10551        {
10552            self.skip(); // consume IN
10553            self.skip(); // consume PARTITION
10554                            // Consume partition expression (string or identifier)
10555            let _partition = self.parse_primary()?;
10556        }
10557
10558        // Parse OUTPUT clause (TSQL) - may have been parsed early (before FROM)
10559        let output = if early_output.is_some() {
10560            early_output
10561        } else if self.match_token(TokenType::Output) {
10562            Some(self.parse_output_clause()?)
10563        } else {
10564            None
10565        };
10566
10567        let where_clause = if self.match_token(TokenType::Where) {
10568            Some(Where {
10569                this: self.parse_expression()?,
10570            })
10571        } else {
10572            None
10573        };
10574
10575        // Parse ORDER BY clause (MySQL)
10576        let order_by = if self.match_keywords(&[TokenType::Order, TokenType::By]) {
10577            Some(self.parse_order_by()?)
10578        } else {
10579            None
10580        };
10581
10582        // Parse LIMIT clause (MySQL)
10583        let limit = if self.match_token(TokenType::Limit) {
10584            Some(self.parse_expression()?)
10585        } else {
10586            None
10587        };
10588
10589        // Parse RETURNING clause (PostgreSQL)
10590        let returning = if self.match_token(TokenType::Returning) {
10591            self.parse_select_expressions()?
10592        } else {
10593            Vec::new()
10594        };
10595
10596        Ok(Expression::Delete(Box::new(Delete {
10597            table,
10598            on_cluster,
10599            alias,
10600            alias_explicit_as,
10601            using,
10602            where_clause,
10603            output,
10604            leading_comments,
10605            with: None,
10606            limit,
10607            order_by,
10608            returning,
10609            tables,
10610            tables_from_using,
10611            joins,
10612            force_index,
10613            no_from: !_has_from,
10614        })))
10615    }
10616
10617    // ==================== DDL Parsing ====================
10618
10619    /// Parse a CREATE statement
10620    fn parse_create(&mut self) -> Result<Expression> {
10621        let create_pos = self.current; // position of CREATE token
10622        let create_token = self.expect(TokenType::Create)?;
10623        let leading_comments = create_token.comments;
10624
10625        // Handle OR REPLACE
10626        let or_replace = self.match_keywords(&[TokenType::Or, TokenType::Replace]);
10627
10628        // Handle TEMPORARY
10629        let temporary = self.match_token(TokenType::Temporary);
10630
10631        // Handle MATERIALIZED
10632        let materialized = self.match_token(TokenType::Materialized);
10633
10634        // Parse MySQL-specific CREATE VIEW options: ALGORITHM, DEFINER, SQL SECURITY
10635        // CREATE ALGORITHM=... DEFINER=... SQL SECURITY DEFINER VIEW ...
10636        let mut algorithm: Option<String> = None;
10637        let mut definer: Option<String> = None;
10638        let mut security: Option<FunctionSecurity> = None;
10639
10640        while self.match_identifier("ALGORITHM")
10641            || self.match_identifier("DEFINER")
10642            || self.match_identifier("SQL")
10643        {
10644            let option_name = self.previous().text.to_ascii_uppercase();
10645
10646            if option_name == "ALGORITHM" && self.match_token(TokenType::Eq) {
10647                // ALGORITHM=UNDEFINED|MERGE|TEMPTABLE
10648                let value = self.expect_identifier_or_keyword()?;
10649                algorithm = Some(value.to_ascii_uppercase());
10650            } else if option_name == "DEFINER" && self.match_token(TokenType::Eq) {
10651                // DEFINER=user@host (can include @ and %)
10652                let mut definer_value = String::new();
10653                while !self.is_at_end()
10654                    && !self.check(TokenType::View)
10655                    && !self.check_identifier("ALGORITHM")
10656                    && !self.check_identifier("DEFINER")
10657                    && !self.check_identifier("SQL")
10658                    && !self.check_identifier("SECURITY")
10659                {
10660                    definer_value.push_str(&self.advance().text);
10661                }
10662                definer = Some(definer_value);
10663            } else if option_name == "SQL" && self.match_identifier("SECURITY") {
10664                // SQL SECURITY DEFINER/INVOKER
10665                if self.match_identifier("DEFINER") {
10666                    security = Some(FunctionSecurity::Definer);
10667                } else if self.match_identifier("INVOKER") {
10668                    security = Some(FunctionSecurity::Invoker);
10669                }
10670            }
10671        }
10672
10673        // Handle SECURE modifier for VIEW (Snowflake)
10674        let secure = self.match_identifier("SECURE");
10675
10676        // Handle table modifiers: DYNAMIC, ICEBERG, EXTERNAL, HYBRID, TRANSIENT (Snowflake), UNLOGGED (PostgreSQL)
10677        let mut table_modifier: Option<String> = if self.check_identifier("DYNAMIC") {
10678            self.skip();
10679            Some("DYNAMIC".to_string())
10680        } else if self.check_identifier("ICEBERG") {
10681            self.skip();
10682            Some("ICEBERG".to_string())
10683        } else if self.check_identifier("EXTERNAL") {
10684            self.skip();
10685            Some("EXTERNAL".to_string())
10686        } else if self.check_identifier("HYBRID") {
10687            self.skip();
10688            Some("HYBRID".to_string())
10689        } else if self.check_identifier("TRANSIENT") {
10690            self.skip();
10691            Some("TRANSIENT".to_string())
10692        } else if self.check_identifier("UNLOGGED") {
10693            self.skip();
10694            Some("UNLOGGED".to_string())
10695        } else if self.check_identifier("DICTIONARY") {
10696            self.skip();
10697            Some("DICTIONARY".to_string())
10698        } else if self.check(TokenType::Dictionary) {
10699            self.skip();
10700            Some("DICTIONARY".to_string())
10701        } else {
10702            None
10703        };
10704
10705        // Teradata: SET/MULTISET/VOLATILE/GLOBAL TEMPORARY modifiers before TABLE
10706        if matches!(
10707            self.config.dialect,
10708            Some(crate::dialects::DialectType::Teradata)
10709        ) {
10710            let mut parts = Vec::new();
10711            loop {
10712                if self.match_token(TokenType::Set) {
10713                    parts.push(self.previous().text.to_ascii_uppercase());
10714                } else if self.match_identifier("MULTISET") {
10715                    parts.push(self.previous().text.to_ascii_uppercase());
10716                } else if self.match_identifier("VOLATILE") {
10717                    parts.push(self.previous().text.to_ascii_uppercase());
10718                } else if self.match_identifier("GLOBAL") {
10719                    parts.push(self.previous().text.to_ascii_uppercase());
10720                } else if self.match_token(TokenType::Temporary) {
10721                    parts.push(self.previous().text.to_ascii_uppercase());
10722                } else {
10723                    break;
10724                }
10725            }
10726            if !parts.is_empty() {
10727                table_modifier = Some(parts.join(" "));
10728            }
10729        }
10730
10731        if table_modifier.as_deref() == Some("DICTIONARY") {
10732            return self.parse_create_table(
10733                or_replace,
10734                temporary,
10735                leading_comments,
10736                table_modifier.as_deref(),
10737            );
10738        }
10739
10740        match self.peek().token_type {
10741            TokenType::Table => {
10742                // Check if this is CREATE TABLE FUNCTION (BigQuery)
10743                if self.current + 1 < self.tokens.len()
10744                    && self.tokens[self.current + 1].token_type == TokenType::Function
10745                {
10746                    self.skip(); // consume TABLE
10747                    return self.parse_create_function(or_replace, temporary, true);
10748                }
10749                let modifier = if materialized {
10750                    Some("MATERIALIZED")
10751                } else {
10752                    table_modifier.as_deref()
10753                };
10754                self.parse_create_table(or_replace, temporary, leading_comments, modifier)
10755            }
10756            TokenType::Dictionary => {
10757                self.parse_create_table(or_replace, temporary, leading_comments, Some("DICTIONARY"))
10758            }
10759            TokenType::View => self.parse_create_view(
10760                or_replace,
10761                materialized,
10762                temporary,
10763                algorithm,
10764                definer,
10765                security,
10766                secure,
10767            ),
10768            TokenType::Unique => {
10769                self.skip(); // consume UNIQUE
10770                                // Check for CLUSTERED/NONCLUSTERED after UNIQUE (TSQL)
10771                let clustered = if self.check_identifier("CLUSTERED") {
10772                    self.skip();
10773                    Some("CLUSTERED".to_string())
10774                } else if self.check_identifier("NONCLUSTERED") {
10775                    self.skip();
10776                    Some("NONCLUSTERED".to_string())
10777                } else {
10778                    None
10779                };
10780                // Check for COLUMNSTORE (TSQL: CREATE UNIQUE NONCLUSTERED COLUMNSTORE INDEX)
10781                if self.check_identifier("COLUMNSTORE") {
10782                    self.skip();
10783                    // Prepend COLUMNSTORE to clustered
10784                    let clustered = clustered
10785                        .map(|c| format!("{} COLUMNSTORE", c))
10786                        .or_else(|| Some("COLUMNSTORE".to_string()));
10787                    self.parse_create_index_with_clustered(true, clustered)
10788                } else {
10789                    self.parse_create_index_with_clustered(true, clustered)
10790                }
10791            }
10792            TokenType::Index => self.parse_create_index_with_clustered(false, None),
10793            TokenType::Schema => self.parse_create_schema(leading_comments),
10794            TokenType::Database => self.parse_create_database(),
10795            TokenType::Function => self.parse_create_function(or_replace, temporary, false),
10796            TokenType::Procedure => self.parse_create_procedure(or_replace),
10797            TokenType::Sequence => self.parse_create_sequence(temporary, or_replace),
10798            TokenType::Trigger => self.parse_create_trigger(or_replace, false, create_pos),
10799            TokenType::Constraint => {
10800                self.skip(); // consume CONSTRAINT
10801                self.parse_create_trigger(or_replace, true, create_pos)
10802            }
10803            TokenType::Type => self.parse_create_type(),
10804            TokenType::Domain => self.parse_create_domain(),
10805            _ => {
10806                // Handle TSQL CLUSTERED/NONCLUSTERED [COLUMNSTORE] INDEX
10807                if self.check_identifier("CLUSTERED") || self.check_identifier("NONCLUSTERED") {
10808                    let clustered_text = self.advance().text.to_ascii_uppercase();
10809                    // Check for COLUMNSTORE after CLUSTERED/NONCLUSTERED
10810                    let clustered = if self.check_identifier("COLUMNSTORE") {
10811                        self.skip();
10812                        Some(format!("{} COLUMNSTORE", clustered_text))
10813                    } else {
10814                        Some(clustered_text)
10815                    };
10816                    return self.parse_create_index_with_clustered(false, clustered);
10817                }
10818                // Handle TSQL COLUMNSTORE INDEX (without CLUSTERED/NONCLUSTERED prefix)
10819                if self.check_identifier("COLUMNSTORE") && {
10820                    let pos = self.current;
10821                    let result = pos + 1 < self.tokens.len()
10822                        && self.tokens[pos + 1].token_type == TokenType::Index;
10823                    result
10824                } {
10825                    self.skip(); // consume COLUMNSTORE
10826                                    // COLUMNSTORE without prefix implies NONCLUSTERED
10827                    return self.parse_create_index_with_clustered(
10828                        false,
10829                        Some("NONCLUSTERED COLUMNSTORE".to_string()),
10830                    );
10831                }
10832                // Handle identifiers that aren't keywords: TAG, STAGE, STREAM, etc.
10833                if self.check_identifier("TAG") {
10834                    return self.parse_create_tag(or_replace);
10835                }
10836                if self.check_identifier("STAGE") {
10837                    return self.parse_create_stage(or_replace, temporary);
10838                }
10839                if self.check_identifier("STREAM") {
10840                    return self.parse_create_stream(or_replace);
10841                }
10842                if (self.check_identifier("FILE") || self.check(TokenType::File)) && {
10843                    let next = self.current + 1;
10844                    next < self.tokens.len()
10845                        && (self.tokens[next].text.eq_ignore_ascii_case("FORMAT"))
10846                } {
10847                    return self.parse_create_file_format(or_replace, temporary);
10848                }
10849                // Fall back to Raw for unrecognized CREATE targets
10850                // (e.g., CREATE WAREHOUSE, CREATE STREAMLIT, CREATE STORAGE INTEGRATION, etc.)
10851                {
10852                    let start = self.current;
10853                    while !self.is_at_end() && !self.check(TokenType::Semicolon) {
10854                        self.skip();
10855                    }
10856                    let sql = self.tokens_to_sql(start, self.current);
10857                    let mut prefix = String::from("CREATE");
10858                    if or_replace {
10859                        prefix.push_str(" OR REPLACE");
10860                    }
10861                    if temporary {
10862                        prefix.push_str(" TEMPORARY");
10863                    }
10864                    if materialized {
10865                        prefix.push_str(" MATERIALIZED");
10866                    }
10867                    prefix.push(' ');
10868                    prefix.push_str(&sql);
10869                    Ok(Expression::Raw(Raw { sql: prefix }))
10870                }
10871            }
10872        }
10873    }
10874
10875    /// Parse CREATE TABLE
10876    fn parse_create_table(
10877        &mut self,
10878        or_replace: bool,
10879        temporary: bool,
10880        leading_comments: Vec<String>,
10881        table_modifier: Option<&str>,
10882    ) -> Result<Expression> {
10883        if table_modifier == Some("DICTIONARY") {
10884            let _ = self.match_token(TokenType::Dictionary);
10885        } else {
10886            self.expect(TokenType::Table)?;
10887        }
10888
10889        // Handle IF NOT EXISTS
10890        let if_not_exists =
10891            self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists]);
10892
10893        let is_special_modifier = matches!(
10894            table_modifier,
10895            Some(
10896                "DYNAMIC"
10897                    | "ICEBERG"
10898                    | "EXTERNAL"
10899                    | "HYBRID"
10900                    | "UNLOGGED"
10901                    | "DICTIONARY"
10902                    | "MATERIALIZED"
10903            )
10904        ) || (table_modifier.is_some()
10905            && matches!(
10906                self.config.dialect,
10907                Some(crate::dialects::DialectType::Teradata)
10908            ));
10909        let is_clickhouse = matches!(
10910            self.config.dialect,
10911            Some(crate::dialects::DialectType::ClickHouse)
10912        );
10913
10914        // Parse table name
10915        let name = self.parse_table_ref()?;
10916
10917        // ClickHouse: UUID 'xxx' clause after table name
10918        if matches!(
10919            self.config.dialect,
10920            Some(crate::dialects::DialectType::ClickHouse)
10921        ) && self.check_identifier("UUID")
10922        {
10923            self.skip(); // consume UUID
10924            let _ = self.advance(); // consume UUID string value
10925        }
10926
10927        // ClickHouse: ON CLUSTER clause
10928        let on_cluster = self.parse_on_cluster_clause()?;
10929
10930        // Teradata: options after name before column list
10931        let teradata_post_name_options = if matches!(
10932            self.config.dialect,
10933            Some(crate::dialects::DialectType::Teradata)
10934        ) {
10935            self.parse_teradata_post_name_options()
10936        } else {
10937            Vec::new()
10938        };
10939
10940        // Handle PARTITION OF parent_table [(column_defs)] [FOR VALUES spec | DEFAULT] [PARTITION BY ...]
10941        if self.match_keywords(&[TokenType::Partition, TokenType::Of]) {
10942            return self.parse_create_table_partition_of(
10943                name,
10944                if_not_exists,
10945                temporary,
10946                or_replace,
10947                table_modifier,
10948                leading_comments,
10949            );
10950        }
10951
10952        // ClickHouse: EMPTY AS source_table — create empty table from source
10953        if matches!(
10954            self.config.dialect,
10955            Some(crate::dialects::DialectType::ClickHouse)
10956        ) && self.check_identifier("EMPTY")
10957        {
10958            if self.check_next(TokenType::As) {
10959                self.skip(); // consume EMPTY
10960                self.skip(); // consume AS
10961                                // Consume rest as Command
10962                let start = self.current;
10963                while !self.is_at_end() && !self.check(TokenType::Semicolon) {
10964                    self.skip();
10965                }
10966                let rest_sql = self.tokens_to_sql(start, self.current);
10967                let mut prefix = String::from("CREATE TABLE");
10968                if if_not_exists {
10969                    prefix.push_str(" IF NOT EXISTS");
10970                }
10971                prefix.push(' ');
10972                prefix.push_str(&name.name.name);
10973                prefix.push_str(" EMPTY AS ");
10974                prefix.push_str(&rest_sql);
10975                return Ok(Expression::Raw(Raw { sql: prefix }));
10976            }
10977        }
10978
10979        // Handle [SHALLOW | DEEP] CLONE source_table [AT(...) | BEFORE(...)]
10980        // Databricks/Delta Lake uses SHALLOW CLONE / DEEP CLONE
10981        // Snowflake uses just CLONE (which is equivalent to DEEP CLONE)
10982        let shallow_clone = self.check_identifier("SHALLOW");
10983        let deep_clone = self.check_identifier("DEEP");
10984        if shallow_clone || deep_clone {
10985            self.skip(); // consume SHALLOW or DEEP
10986        }
10987        // Also handle COPY (BigQuery: CREATE TABLE ... COPY source_table)
10988        // But NOT "COPY GRANTS" which is a Snowflake property
10989        let is_copy = self.check(TokenType::Copy) && !self.check_next_identifier("GRANTS");
10990        if self.check_identifier("CLONE") || is_copy {
10991            self.skip(); // consume CLONE or COPY
10992                            // ClickHouse: CLONE AS source_table (AS is part of the syntax, not an alias)
10993            if matches!(
10994                self.config.dialect,
10995                Some(crate::dialects::DialectType::ClickHouse)
10996            ) {
10997                let _ = self.match_token(TokenType::As);
10998            }
10999            let source = self.parse_table_ref()?;
11000            // Parse optional AT or BEFORE time travel clause
11001            // Note: BEFORE is a keyword token, AT is an identifier
11002            let at_clause = if self.match_identifier("AT") || self.match_token(TokenType::Before) {
11003                let keyword = self.previous().text.to_ascii_uppercase();
11004                self.expect(TokenType::LParen)?;
11005                // Parse the content: OFFSET => value or TIMESTAMP => value
11006                let mut result = format!("{} (", keyword);
11007                let mut prev_token_type: Option<TokenType> = None;
11008                let mut paren_depth = 1;
11009                while !self.is_at_end() && paren_depth > 0 {
11010                    let token = self.advance();
11011                    if token.token_type == TokenType::LParen {
11012                        paren_depth += 1;
11013                    } else if token.token_type == TokenType::RParen {
11014                        paren_depth -= 1;
11015                        if paren_depth == 0 {
11016                            break;
11017                        }
11018                    }
11019                    let needs_space = !result.ends_with('(')
11020                        && prev_token_type != Some(TokenType::Arrow)
11021                        && prev_token_type != Some(TokenType::Dash)
11022                        && prev_token_type != Some(TokenType::LParen)
11023                        && prev_token_type != Some(TokenType::Comma) // comma already adds trailing space
11024                        && token.token_type != TokenType::LParen; // no space before (
11025                    if needs_space
11026                        && token.token_type != TokenType::RParen
11027                        && token.token_type != TokenType::Comma
11028                    {
11029                        result.push(' ');
11030                    }
11031                    // Properly quote string literals
11032                    if token.token_type == TokenType::String {
11033                        result.push('\'');
11034                        result.push_str(&token.text.replace('\'', "''"));
11035                        result.push('\'');
11036                    } else {
11037                        result.push_str(&token.text);
11038                    }
11039                    if token.token_type == TokenType::Arrow || token.token_type == TokenType::Comma
11040                    {
11041                        result.push(' ');
11042                    }
11043                    prev_token_type = Some(token.token_type);
11044                }
11045                result.push(')');
11046                Some(Expression::Raw(Raw { sql: result }))
11047            } else {
11048                None
11049            };
11050            // Return the CLONE table immediately
11051            return Ok(Expression::CreateTable(Box::new(CreateTable {
11052                name,
11053                on_cluster: on_cluster.clone(),
11054                columns: Vec::new(),
11055                constraints: Vec::new(),
11056                if_not_exists,
11057                temporary,
11058                or_replace,
11059                table_modifier: table_modifier.map(|s| s.to_string()),
11060                as_select: None,
11061                as_select_parenthesized: false,
11062                on_commit: None,
11063                clone_source: Some(source),
11064                clone_at_clause: at_clause,
11065                shallow_clone,
11066                is_copy,
11067                leading_comments,
11068                with_properties: Vec::new(),
11069                teradata_post_name_options: teradata_post_name_options.clone(),
11070                with_data: None,
11071                with_statistics: None,
11072                teradata_indexes: Vec::new(),
11073                with_cte: None,
11074                properties: Vec::new(),
11075                partition_of: None,
11076                post_table_properties: Vec::new(),
11077                mysql_table_options: Vec::new(),
11078                inherits: Vec::new(),
11079                on_property: None,
11080                copy_grants: false,
11081                using_template: None,
11082                rollup: None,
11083            })));
11084        }
11085
11086        // Handle WITH properties before columns/AS (e.g., CREATE TABLE z WITH (FORMAT='parquet') AS SELECT 1)
11087        let with_properties = if self.match_token(TokenType::With) {
11088            self.parse_with_properties()?
11089        } else {
11090            Vec::new()
11091        };
11092
11093        // Snowflake: COPY GRANTS clause (before column list or AS)
11094        let copy_grants = self.match_text_seq(&["COPY", "GRANTS"]);
11095
11096        // Snowflake: USING TEMPLATE (expr) - allows schema inference from a query
11097        let using_template = if self.match_text_seq(&["USING", "TEMPLATE"]) {
11098            Some(Box::new(self.parse_primary()?))
11099        } else {
11100            None
11101        };
11102
11103        // If we have USING TEMPLATE, return early since it replaces AS SELECT
11104        if using_template.is_some() {
11105            return Ok(Expression::CreateTable(Box::new(CreateTable {
11106                name,
11107                on_cluster: on_cluster.clone(),
11108                columns: Vec::new(),
11109                constraints: Vec::new(),
11110                if_not_exists,
11111                temporary,
11112                or_replace,
11113                table_modifier: table_modifier.map(|s| s.to_string()),
11114                as_select: None,
11115                as_select_parenthesized: false,
11116                on_commit: None,
11117                clone_source: None,
11118                clone_at_clause: None,
11119                shallow_clone: false,
11120                is_copy: false,
11121                leading_comments,
11122                with_properties,
11123                teradata_post_name_options: teradata_post_name_options.clone(),
11124                with_data: None,
11125                with_statistics: None,
11126                teradata_indexes: Vec::new(),
11127                with_cte: None,
11128                properties: Vec::new(),
11129                partition_of: None,
11130                post_table_properties: Vec::new(),
11131                mysql_table_options: Vec::new(),
11132                inherits: Vec::new(),
11133                on_property: None,
11134                copy_grants,
11135                using_template,
11136                rollup: None,
11137            })));
11138        }
11139
11140        // Redshift: Parse DISTKEY, SORTKEY, DISTSTYLE, BACKUP before AS SELECT (CTAS without columns)
11141        // This handles: CREATE TABLE t BACKUP YES|NO AS SELECT ...
11142        let mut redshift_ctas_properties: Vec<Expression> = Vec::new();
11143        loop {
11144            if self.match_identifier("DISTKEY") {
11145                // DISTKEY(column)
11146                if self.match_token(TokenType::LParen) {
11147                    let col = self.expect_identifier()?;
11148                    self.expect(TokenType::RParen)?;
11149                    redshift_ctas_properties.push(Expression::DistKeyProperty(Box::new(
11150                        DistKeyProperty {
11151                            this: Box::new(Expression::boxed_column(Column {
11152                                name: Identifier::new(col),
11153                                table: None,
11154                                join_mark: false,
11155                                trailing_comments: Vec::new(),
11156                                span: None,
11157                                inferred_type: None,
11158                            })),
11159                        },
11160                    )));
11161                }
11162            } else if self.check_identifier("COMPOUND") || self.check_identifier("INTERLEAVED") {
11163                // COMPOUND SORTKEY(col, ...) or INTERLEAVED SORTKEY(col, ...)
11164                let modifier = self.advance().text.to_ascii_uppercase();
11165                if self.match_identifier("SORTKEY") && self.match_token(TokenType::LParen) {
11166                    let mut cols = Vec::new();
11167                    loop {
11168                        let col = self.expect_identifier()?;
11169                        cols.push(Expression::boxed_column(Column {
11170                            name: Identifier::new(col),
11171                            table: None,
11172                            join_mark: false,
11173                            trailing_comments: Vec::new(),
11174                            span: None,
11175                            inferred_type: None,
11176                        }));
11177                        if !self.match_token(TokenType::Comma) {
11178                            break;
11179                        }
11180                    }
11181                    self.expect(TokenType::RParen)?;
11182                    let compound_value = if modifier == "COMPOUND" {
11183                        Some(Box::new(Expression::Boolean(BooleanLiteral {
11184                            value: true,
11185                        })))
11186                    } else {
11187                        None
11188                    };
11189                    redshift_ctas_properties.push(Expression::SortKeyProperty(Box::new(
11190                        SortKeyProperty {
11191                            this: Box::new(Expression::Tuple(Box::new(Tuple {
11192                                expressions: cols,
11193                            }))),
11194                            compound: compound_value,
11195                        },
11196                    )));
11197                }
11198            } else if self.match_identifier("SORTKEY") {
11199                // SORTKEY(column, ...)
11200                if self.match_token(TokenType::LParen) {
11201                    let mut cols = Vec::new();
11202                    loop {
11203                        let col = self.expect_identifier()?;
11204                        cols.push(Expression::boxed_column(Column {
11205                            name: Identifier::new(col),
11206                            table: None,
11207                            join_mark: false,
11208                            trailing_comments: Vec::new(),
11209                            span: None,
11210                            inferred_type: None,
11211                        }));
11212                        if !self.match_token(TokenType::Comma) {
11213                            break;
11214                        }
11215                    }
11216                    self.expect(TokenType::RParen)?;
11217                    redshift_ctas_properties.push(Expression::SortKeyProperty(Box::new(
11218                        SortKeyProperty {
11219                            this: Box::new(Expression::Tuple(Box::new(Tuple {
11220                                expressions: cols,
11221                            }))),
11222                            compound: None,
11223                        },
11224                    )));
11225                }
11226            } else if self.match_identifier("DISTSTYLE") {
11227                // DISTSTYLE ALL|EVEN|AUTO|KEY
11228                if self.match_texts(&["ALL", "EVEN", "AUTO", "KEY"]) {
11229                    let style = self.previous().text.to_ascii_uppercase();
11230                    redshift_ctas_properties.push(Expression::DistStyleProperty(Box::new(
11231                        DistStyleProperty {
11232                            this: Box::new(Expression::Var(Box::new(Var { this: style }))),
11233                        },
11234                    )));
11235                }
11236            } else if self.match_identifier("BACKUP") {
11237                // BACKUP YES|NO
11238                if self.match_texts(&["YES", "NO"]) {
11239                    let value = self.previous().text.to_ascii_uppercase();
11240                    redshift_ctas_properties.push(Expression::BackupProperty(Box::new(
11241                        BackupProperty {
11242                            this: Box::new(Expression::Var(Box::new(Var { this: value }))),
11243                        },
11244                    )));
11245                }
11246            } else {
11247                break;
11248            }
11249        }
11250
11251        // Check for AS SELECT (CTAS)
11252        if self.match_token(TokenType::As) {
11253            // ClickHouse: CREATE TABLE t AS other_table [ENGINE = ...] — copy structure from another table
11254            // Also: CREATE TABLE t AS func_name(args...) — table from function (e.g., remote, merge)
11255            // Detect when AS is followed by an identifier (not SELECT/WITH/LParen)
11256            if is_clickhouse
11257                && !self.check(TokenType::Select)
11258                && !self.check(TokenType::With)
11259                && !self.check(TokenType::LParen)
11260                && (self.is_identifier_token() || self.is_safe_keyword_as_identifier())
11261            {
11262                // Check if this is AS func_name(...) — table function
11263                let is_table_func = self.current + 1 < self.tokens.len()
11264                    && self.tokens[self.current + 1].token_type == TokenType::LParen;
11265                let source = if is_table_func {
11266                    // Parse as expression to consume function call with arguments
11267                    self.parse_primary()?;
11268                    let mut table_properties: Vec<Expression> = Vec::new();
11269                    self.parse_clickhouse_table_properties(&mut table_properties)?;
11270                    return Ok(Expression::CreateTable(Box::new(CreateTable {
11271                        name,
11272                        on_cluster: on_cluster.clone(),
11273                        columns: Vec::new(),
11274                        constraints: Vec::new(),
11275                        if_not_exists,
11276                        temporary,
11277                        or_replace,
11278                        table_modifier: table_modifier.map(|s| s.to_string()),
11279                        as_select: None,
11280                        as_select_parenthesized: false,
11281                        on_commit: None,
11282                        clone_source: None,
11283                        clone_at_clause: None,
11284                        shallow_clone: false,
11285                        is_copy: false,
11286                        leading_comments,
11287                        with_properties,
11288                        teradata_post_name_options: teradata_post_name_options.clone(),
11289                        with_data: None,
11290                        with_statistics: None,
11291                        teradata_indexes: Vec::new(),
11292                        with_cte: None,
11293                        properties: table_properties,
11294                        partition_of: None,
11295                        post_table_properties: redshift_ctas_properties,
11296                        mysql_table_options: Vec::new(),
11297                        inherits: Vec::new(),
11298                        on_property: None,
11299                        copy_grants,
11300                        using_template: None,
11301                        rollup: None,
11302                    })));
11303                } else {
11304                    self.parse_table_ref()?
11305                };
11306                // Parse ClickHouse table properties after the source table
11307                let mut table_properties: Vec<Expression> = Vec::new();
11308                self.parse_clickhouse_table_properties(&mut table_properties)?;
11309                return Ok(Expression::CreateTable(Box::new(CreateTable {
11310                    name,
11311                    on_cluster: on_cluster.clone(),
11312                    columns: Vec::new(),
11313                    constraints: Vec::new(),
11314                    if_not_exists,
11315                    temporary,
11316                    or_replace,
11317                    table_modifier: table_modifier.map(|s| s.to_string()),
11318                    as_select: None,
11319                    as_select_parenthesized: false,
11320                    on_commit: None,
11321                    clone_source: Some(source),
11322                    clone_at_clause: None,
11323                    shallow_clone: false,
11324                    is_copy: false,
11325                    leading_comments,
11326                    with_properties,
11327                    teradata_post_name_options: teradata_post_name_options.clone(),
11328                    with_data: None,
11329                    with_statistics: None,
11330                    teradata_indexes: Vec::new(),
11331                    with_cte: None,
11332                    properties: table_properties,
11333                    partition_of: None,
11334                    post_table_properties: redshift_ctas_properties,
11335                    mysql_table_options: Vec::new(),
11336                    inherits: Vec::new(),
11337                    on_property: None,
11338                    copy_grants,
11339                    using_template: None,
11340                    rollup: None,
11341                })));
11342            }
11343
11344            // The query can be:
11345            // - SELECT ... (simple case)
11346            // - (SELECT 1) UNION ALL (SELECT 2) (set operations)
11347            // - (WITH cte AS (SELECT 1) SELECT * FROM cte) (CTE in parens)
11348            let mut as_select_parenthesized = self.check(TokenType::LParen);
11349            let query = if as_select_parenthesized {
11350                // Parenthesized query - parse as expression which handles subqueries
11351                // Note: parse_primary will consume set operations like UNION internally
11352                let subquery = self.parse_primary()?;
11353                // If parse_primary returned a set operation, the outer parens weren't wrapping
11354                // the entire expression - they were part of the operands
11355                if matches!(
11356                    &subquery,
11357                    Expression::Union(_) | Expression::Intersect(_) | Expression::Except(_)
11358                ) {
11359                    as_select_parenthesized = false;
11360                    subquery
11361                } else {
11362                    // Just a parenthesized query without set ops
11363                    // Keep the Subquery wrapper if it has limit/offset/order_by
11364                    if let Expression::Subquery(ref sq) = subquery {
11365                        if sq.limit.is_some() || sq.offset.is_some() || sq.order_by.is_some() {
11366                            // Keep the Subquery to preserve the modifiers
11367                            subquery
11368                        } else {
11369                            // Extract the inner query
11370                            if let Expression::Subquery(sq) = subquery {
11371                                sq.this
11372                            } else {
11373                                subquery
11374                            }
11375                        }
11376                    } else if let Expression::Paren(p) = subquery {
11377                        p.this
11378                    } else {
11379                        subquery
11380                    }
11381                }
11382            } else if self.check(TokenType::With) {
11383                // Handle WITH ... SELECT ...
11384                self.parse_statement()?
11385            } else {
11386                self.parse_select()?
11387            };
11388
11389            // Parse any trailing Teradata options like "WITH DATA", "NO PRIMARY INDEX", etc.
11390            let (with_data, with_statistics, teradata_indexes) =
11391                self.parse_teradata_table_options();
11392            let on_commit = if matches!(
11393                self.config.dialect,
11394                Some(crate::dialects::DialectType::Teradata)
11395            ) && self.check(TokenType::On)
11396                && self.check_next(TokenType::Commit)
11397            {
11398                self.skip(); // ON
11399                self.skip(); // COMMIT
11400                if self.match_keywords(&[TokenType::Preserve, TokenType::Rows]) {
11401                    Some(OnCommit::PreserveRows)
11402                } else if self.match_keywords(&[TokenType::Delete, TokenType::Rows]) {
11403                    Some(OnCommit::DeleteRows)
11404                } else {
11405                    return Err(
11406                        self.parse_error("Expected PRESERVE ROWS or DELETE ROWS after ON COMMIT")
11407                    );
11408                }
11409            } else {
11410                None
11411            };
11412
11413            return Ok(Expression::CreateTable(Box::new(CreateTable {
11414                name,
11415                on_cluster: on_cluster.clone(),
11416                columns: Vec::new(),
11417                constraints: Vec::new(),
11418                if_not_exists,
11419                temporary,
11420                or_replace,
11421                table_modifier: table_modifier.map(|s| s.to_string()),
11422                as_select: Some(query),
11423                as_select_parenthesized,
11424                on_commit,
11425                clone_source: None,
11426                clone_at_clause: None,
11427                shallow_clone: false,
11428                is_copy: false,
11429                leading_comments,
11430                with_properties,
11431                teradata_post_name_options: teradata_post_name_options.clone(),
11432                with_data,
11433                with_statistics,
11434                teradata_indexes,
11435                with_cte: None,
11436                properties: Vec::new(),
11437                partition_of: None,
11438                post_table_properties: redshift_ctas_properties,
11439                mysql_table_options: Vec::new(),
11440                inherits: Vec::new(),
11441                on_property: None,
11442                copy_grants,
11443                using_template: None,
11444                rollup: None,
11445            })));
11446        }
11447
11448        // ClickHouse: allow table properties/AS SELECT without a column list
11449        if is_clickhouse && !self.check(TokenType::LParen) {
11450            let starts_props = self.check_identifier("ENGINE")
11451                || self.check(TokenType::Order)
11452                || self.check(TokenType::Sample)
11453                || self.check(TokenType::Settings)
11454                || self.check(TokenType::Comment)
11455                || self.check(TokenType::As);
11456
11457            if starts_props {
11458                let mut table_properties: Vec<Expression> = Vec::new();
11459                self.parse_clickhouse_table_properties(&mut table_properties)?;
11460
11461                let as_select = if self.match_token(TokenType::As) {
11462                    Some(self.parse_statement()?)
11463                } else {
11464                    None
11465                };
11466                let as_select_parenthesized = as_select.is_some();
11467
11468                if as_select.is_some() {
11469                    self.parse_clickhouse_table_properties(&mut table_properties)?;
11470                }
11471
11472                return Ok(Expression::CreateTable(Box::new(CreateTable {
11473                    name,
11474                    on_cluster: on_cluster.clone(),
11475                    columns: Vec::new(),
11476                    constraints: Vec::new(),
11477                    if_not_exists,
11478                    temporary,
11479                    or_replace,
11480                    table_modifier: table_modifier.map(|s| s.to_string()),
11481                    as_select,
11482                    as_select_parenthesized,
11483                    on_commit: None,
11484                    clone_source: None,
11485                    clone_at_clause: None,
11486                    shallow_clone: false,
11487                    is_copy: false,
11488                    leading_comments,
11489                    with_properties,
11490                    teradata_post_name_options: teradata_post_name_options.clone(),
11491                    with_data: None,
11492                    with_statistics: None,
11493                    teradata_indexes: Vec::new(),
11494                    with_cte: None,
11495                    properties: table_properties,
11496                    partition_of: None,
11497                    post_table_properties: Vec::new(),
11498                    mysql_table_options: Vec::new(),
11499                    inherits: Vec::new(),
11500                    on_property: None,
11501                    copy_grants,
11502                    using_template: None,
11503                    rollup: None,
11504                })));
11505            }
11506        }
11507
11508        // For DYNAMIC/ICEBERG/EXTERNAL tables, columns might be optional (use AS SELECT or other syntax)
11509        // Check if we have a left paren for columns or if we're going straight to options
11510        if !self.check(TokenType::LParen) && is_special_modifier {
11511            // No columns - parse options and AS SELECT
11512            let mut extra_options = Vec::new();
11513            // Parse key=value options until AS or end
11514            // Note: WAREHOUSE is a keyword token type, so check for it explicitly
11515            while !self.is_at_end()
11516                && !self.check(TokenType::As)
11517                && !self.check(TokenType::Semicolon)
11518            {
11519                if self.is_identifier_token()
11520                    || self.is_safe_keyword_as_identifier()
11521                    || self.check(TokenType::Warehouse)
11522                {
11523                    let key = self.advance().text;
11524                    if self.match_token(TokenType::Eq) {
11525                        // Capture value
11526                        let value = if self.check(TokenType::String) {
11527                            let v = format!("'{}'", self.peek().text);
11528                            self.skip();
11529                            v
11530                        } else if self.is_identifier_token() || self.is_safe_keyword_as_identifier()
11531                        {
11532                            self.advance().text
11533                        } else {
11534                            break;
11535                        };
11536                        extra_options.push((key, value));
11537                    } else {
11538                        // Just a keyword without value (like WAREHOUSE mywh)
11539                        if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
11540                            let value = self.advance().text;
11541                            extra_options.push((key, value));
11542                        }
11543                    }
11544                } else {
11545                    break;
11546                }
11547            }
11548            // Check for AS SELECT
11549            let as_select = if self.match_token(TokenType::As) {
11550                Some(self.parse_statement()?)
11551            } else {
11552                None
11553            };
11554            return Ok(Expression::CreateTable(Box::new(CreateTable {
11555                name,
11556                on_cluster: on_cluster.clone(),
11557                columns: Vec::new(),
11558                constraints: Vec::new(),
11559                if_not_exists,
11560                temporary,
11561                or_replace,
11562                table_modifier: table_modifier.map(|s| s.to_string()),
11563                as_select,
11564                as_select_parenthesized: false,
11565                on_commit: None,
11566                clone_source: None,
11567                clone_at_clause: None,
11568                shallow_clone: false,
11569                is_copy: false,
11570                leading_comments,
11571                with_properties: extra_options,
11572                teradata_post_name_options: teradata_post_name_options.clone(),
11573                with_data: None,
11574                with_statistics: None,
11575                teradata_indexes: Vec::new(),
11576                with_cte: None,
11577                properties: Vec::new(),
11578                partition_of: None,
11579                post_table_properties: Vec::new(),
11580                mysql_table_options: Vec::new(),
11581                inherits: Vec::new(),
11582                on_property: None,
11583                copy_grants,
11584                using_template: None,
11585                rollup: None,
11586            })));
11587        }
11588
11589        // MySQL: CREATE TABLE A LIKE B (without parentheses)
11590        if self.check(TokenType::Like) {
11591            self.skip(); // consume LIKE
11592            let source_ref = self.parse_table_ref()?;
11593            return Ok(Expression::CreateTable(Box::new(CreateTable {
11594                name,
11595                on_cluster: on_cluster.clone(),
11596                columns: Vec::new(),
11597                constraints: vec![TableConstraint::Like {
11598                    source: source_ref,
11599                    options: Vec::new(),
11600                }],
11601                if_not_exists,
11602                temporary,
11603                or_replace,
11604                table_modifier: table_modifier.map(|s| s.to_string()),
11605                as_select: None,
11606                as_select_parenthesized: false,
11607                on_commit: None,
11608                clone_source: None,
11609                clone_at_clause: None,
11610                shallow_clone: false,
11611                is_copy: false,
11612                leading_comments,
11613                with_properties,
11614                teradata_post_name_options: teradata_post_name_options.clone(),
11615                with_data: None,
11616                with_statistics: None,
11617                teradata_indexes: Vec::new(),
11618                with_cte: None,
11619                properties: Vec::new(),
11620                partition_of: None,
11621                post_table_properties: Vec::new(),
11622                mysql_table_options: Vec::new(),
11623                inherits: Vec::new(),
11624                on_property: None,
11625                copy_grants,
11626                using_template: None,
11627                rollup: None,
11628            })));
11629        }
11630
11631        // Snowflake: CREATE TABLE a TAG (key='value', ...) without column definitions
11632        if self.match_keyword("TAG")
11633            || (self.match_token(TokenType::With) && self.match_keyword("TAG"))
11634        {
11635            let tags = self.parse_tags()?;
11636            return Ok(Expression::CreateTable(Box::new(CreateTable {
11637                name,
11638                on_cluster: on_cluster.clone(),
11639                columns: Vec::new(),
11640                constraints: vec![TableConstraint::Tags(tags)],
11641                if_not_exists,
11642                temporary,
11643                or_replace,
11644                table_modifier: table_modifier.map(|s| s.to_string()),
11645                as_select: None,
11646                as_select_parenthesized: false,
11647                on_commit: None,
11648                clone_source: None,
11649                clone_at_clause: None,
11650                shallow_clone: false,
11651                is_copy: false,
11652                leading_comments,
11653                with_properties,
11654                teradata_post_name_options: teradata_post_name_options.clone(),
11655                with_data: None,
11656                with_statistics: None,
11657                teradata_indexes: Vec::new(),
11658                with_cte: None,
11659                properties: Vec::new(),
11660                partition_of: None,
11661                post_table_properties: Vec::new(),
11662                mysql_table_options: Vec::new(),
11663                inherits: Vec::new(),
11664                on_property: None,
11665                copy_grants,
11666                using_template: None,
11667                rollup: None,
11668            })));
11669        }
11670
11671        // Hive/Spark/Databricks: CREATE TABLE t TBLPROPERTIES (...) without column definitions
11672        // Check for Hive-style table properties before expecting column definitions
11673        if self.check_identifier("TBLPROPERTIES")
11674            || self.check_identifier("LOCATION")
11675            || self.check_identifier("STORED")
11676            || self.check(TokenType::Row)
11677            || self.check(TokenType::Using)
11678            || self.check_identifier("CLUSTERED")
11679            || self.check_identifier("PARTITIONED")
11680            || self.check_identifier("COMMENT")
11681        {
11682            // Parse Hive table properties without column definitions
11683            let hive_properties = self.parse_hive_table_properties()?;
11684
11685            // Check for AS SELECT (CTAS) after properties
11686            let as_select = if self.match_token(TokenType::As) {
11687                Some(self.parse_statement()?)
11688            } else {
11689                None
11690            };
11691
11692            return Ok(Expression::CreateTable(Box::new(CreateTable {
11693                name,
11694                on_cluster: on_cluster.clone(),
11695                columns: Vec::new(),
11696                constraints: Vec::new(),
11697                if_not_exists,
11698                temporary,
11699                or_replace,
11700                table_modifier: table_modifier.map(|s| s.to_string()),
11701                as_select,
11702                as_select_parenthesized: false,
11703                on_commit: None,
11704                clone_source: None,
11705                clone_at_clause: None,
11706                shallow_clone: false,
11707                is_copy: false,
11708                leading_comments,
11709                with_properties,
11710                teradata_post_name_options: teradata_post_name_options.clone(),
11711                with_data: None,
11712                with_statistics: None,
11713                teradata_indexes: Vec::new(),
11714                with_cte: None,
11715                properties: hive_properties,
11716                partition_of: None,
11717                post_table_properties: Vec::new(),
11718                mysql_table_options: Vec::new(),
11719                inherits: Vec::new(),
11720                on_property: None,
11721                copy_grants,
11722                using_template: None,
11723                rollup: None,
11724            })));
11725        }
11726
11727        // Check if (SELECT ...) or (WITH ...) follows - this is CTAS without explicit AS keyword
11728        if self.check(TokenType::LParen) {
11729            let saved = self.current;
11730            self.skip(); // consume (
11731            let is_ctas = self.check(TokenType::Select) || self.check(TokenType::With);
11732            self.current = saved;
11733            if is_ctas {
11734                // Parse as subquery
11735                let subquery = self.parse_primary()?;
11736                let query = if let Expression::Subquery(sq) = subquery {
11737                    sq.this
11738                } else if let Expression::Paren(p) = subquery {
11739                    p.this
11740                } else {
11741                    subquery
11742                };
11743                return Ok(Expression::CreateTable(Box::new(CreateTable {
11744                    name,
11745                    on_cluster: on_cluster.clone(),
11746                    columns: Vec::new(),
11747                    constraints: Vec::new(),
11748                    if_not_exists,
11749                    temporary,
11750                    or_replace,
11751                    table_modifier: table_modifier.map(|s| s.to_string()),
11752                    as_select: Some(query),
11753                    as_select_parenthesized: true,
11754                    on_commit: None,
11755                    clone_source: None,
11756                    clone_at_clause: None,
11757                    shallow_clone: false,
11758                    is_copy: false,
11759                    leading_comments,
11760                    with_properties,
11761                    teradata_post_name_options: teradata_post_name_options.clone(),
11762                    with_data: None,
11763                    with_statistics: None,
11764                    teradata_indexes: Vec::new(),
11765                    with_cte: None,
11766                    properties: Vec::new(),
11767                    partition_of: None,
11768                    post_table_properties: Vec::new(),
11769                    mysql_table_options: Vec::new(),
11770                    inherits: Vec::new(),
11771                    on_property: None,
11772                    copy_grants,
11773                    using_template: None,
11774                    rollup: None,
11775                })));
11776            }
11777        }
11778
11779        // BigQuery (and others): CREATE TABLE t PARTITION BY ... CLUSTER BY ... OPTIONS(...) AS (SELECT ...)
11780        // When there are no column definitions, skip straight to property/AS parsing
11781        let no_column_defs = !self.check(TokenType::LParen)
11782            && (self.check(TokenType::Partition)
11783                || self.check(TokenType::PartitionBy)
11784                || self.check(TokenType::Cluster)
11785                || self.check_identifier("OPTIONS")
11786                || self.check(TokenType::As));
11787
11788        // Parse column definitions
11789        if !no_column_defs {
11790            self.expect(TokenType::LParen)?;
11791        }
11792
11793        // For DYNAMIC TABLE, column list contains only names without types
11794        // e.g., CREATE DYNAMIC TABLE t (col1, col2, col3) TARGET_LAG=... AS SELECT ...
11795        let (columns, constraints) = if no_column_defs {
11796            (Vec::new(), Vec::new())
11797        } else if table_modifier == Some("DYNAMIC") {
11798            // Check if this looks like a simple column name list (just identifiers separated by commas)
11799            // by peeking ahead - if next token after identifier is comma or rparen, it's a name-only list
11800            let saved = self.current;
11801            let is_name_only_list =
11802                if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
11803                    self.skip();
11804                    let result = self.check(TokenType::Comma) || self.check(TokenType::RParen);
11805                    self.current = saved;
11806                    result
11807                } else {
11808                    false
11809                };
11810
11811            if is_name_only_list {
11812                // Parse column names without types
11813                let mut cols = Vec::new();
11814                loop {
11815                    let name = self.expect_identifier_or_safe_keyword_with_quoted()?;
11816                    // Create a column def with an empty/placeholder type
11817                    let mut col_def = ColumnDef::new(
11818                        name.name.clone(),
11819                        DataType::Custom {
11820                            name: String::new(),
11821                        },
11822                    );
11823                    col_def.name = name;
11824                    cols.push(col_def);
11825                    if !self.match_token(TokenType::Comma) {
11826                        break;
11827                    }
11828                }
11829                (cols, Vec::new())
11830            } else {
11831                // Regular column definitions with types
11832                self.parse_column_definitions()?
11833            }
11834        } else {
11835            self.parse_column_definitions()?
11836        };
11837
11838        if !no_column_defs {
11839            self.expect(TokenType::RParen)?;
11840        }
11841
11842        // Parse COMMENT before WITH properties (Presto: CREATE TABLE x (...) COMMENT 'text' WITH (...))
11843        let pre_with_comment = if self.check(TokenType::Comment) {
11844            let saved = self.current;
11845            self.skip(); // consume COMMENT
11846            if self.check(TokenType::String) {
11847                let comment_text = self.advance().text.clone();
11848                Some(comment_text)
11849            } else {
11850                self.current = saved;
11851                None
11852            }
11853        } else {
11854            None
11855        };
11856
11857        // Handle WITH properties after columns (e.g., CREATE TABLE z (z INT) WITH (...))
11858        // But skip if this is WITH(SYSTEM_VERSIONING=...) which is handled by parse_post_table_properties
11859        let with_properties_after = if self.check(TokenType::With) {
11860            // Lookahead: check if this is WITH(SYSTEM_VERSIONING=...)
11861            let saved = self.current;
11862            self.skip(); // consume WITH
11863            let is_system_versioning = if self.check(TokenType::LParen) {
11864                let saved2 = self.current;
11865                self.skip(); // consume (
11866                let result = self.check_identifier("SYSTEM_VERSIONING");
11867                self.current = saved2; // retreat to before (
11868                result
11869            } else {
11870                false
11871            };
11872            if is_system_versioning {
11873                // Retreat back before WITH, let parse_post_table_properties handle it
11874                self.current = saved;
11875                Vec::new()
11876            } else {
11877                // Normal WITH properties parsing
11878                self.parse_with_properties()?
11879            }
11880        } else {
11881            Vec::new()
11882        };
11883
11884        // Combine properties from before and after columns
11885        let mut all_with_properties = with_properties;
11886        all_with_properties.extend(with_properties_after);
11887
11888        // For DYNAMIC/ICEBERG/EXTERNAL tables with columns, parse Snowflake-specific options
11889        // like TARGET_LAG, WAREHOUSE, CATALOG, EXTERNAL_VOLUME, LOCATION etc.
11890        if is_special_modifier {
11891            while !self.is_at_end()
11892                && !self.check(TokenType::As)
11893                && !self.check(TokenType::Semicolon)
11894            {
11895                // Check for known Snowflake table options (WAREHOUSE is a keyword, others are identifiers)
11896                // These are Snowflake-style options that use KEY=VALUE or KEY VALUE (without =)
11897                // Hive-style LOCATION/TBLPROPERTIES (without =) should NOT be matched here
11898                let is_snowflake_option = self.check(TokenType::Warehouse)
11899                    || self.check_identifier("TARGET_LAG")
11900                    || self.check_identifier("CATALOG")
11901                    || self.check_identifier("EXTERNAL_VOLUME")
11902                    || self.check_identifier("BASE_LOCATION")
11903                    || self.check_identifier("REFRESH_MODE")
11904                    || self.check_identifier("INITIALIZE")
11905                    || self.check_identifier("DATA_RETENTION_TIME_IN_DAYS")
11906                    || self.check_identifier("LOCATION")
11907                    || self.check_identifier("PARTITION")
11908                    || self.check_identifier("FILE_FORMAT")
11909                    || self.check_identifier("AUTO_REFRESH");
11910                if is_snowflake_option {
11911                    // Save position before consuming key - we might need to retreat for Hive-style syntax
11912                    let saved = self.current;
11913                    let key = self.advance().text;
11914                    if self.match_token(TokenType::Eq) {
11915                        // Capture value - could be string, identifier, stage path @..., keyword, or parenthesized options
11916                        let value = if self.check(TokenType::LParen) {
11917                            // Parenthesized option list like file_format = (type = parquet compression = gzip)
11918                            self.skip(); // consume (
11919                            let mut options = String::from("(");
11920                            let mut depth = 1;
11921                            while !self.is_at_end() && depth > 0 {
11922                                let tok = self.advance();
11923                                if tok.token_type == TokenType::LParen {
11924                                    depth += 1;
11925                                } else if tok.token_type == TokenType::RParen {
11926                                    depth -= 1;
11927                                }
11928                                // Add space before tokens that need it (not after open paren, not before close paren)
11929                                if !options.ends_with('(')
11930                                    && !options.ends_with(' ')
11931                                    && tok.token_type != TokenType::RParen
11932                                {
11933                                    options.push(' ');
11934                                }
11935                                options.push_str(&tok.text);
11936                            }
11937                            options
11938                        } else if self.check(TokenType::String) {
11939                            let v = format!("'{}'", self.peek().text);
11940                            self.skip();
11941                            v
11942                        } else if self.check(TokenType::DAt) {
11943                            // Stage path like @s1/logs/
11944                            self.skip(); // consume @
11945                            let mut path = String::from("@");
11946                            if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
11947                                path.push_str(&self.advance().text);
11948                            }
11949                            // Parse path segments, but stop before Snowflake option keywords
11950                            while self.check(TokenType::Slash) {
11951                                // Peek ahead to see if next identifier is a Snowflake option keyword
11952                                if self.current + 1 < self.tokens.len() {
11953                                    let next = &self.tokens[self.current + 1];
11954                                    if next.text.eq_ignore_ascii_case("FILE_FORMAT")
11955                                        || next.text.eq_ignore_ascii_case("PARTITION_TYPE")
11956                                        || next.text.eq_ignore_ascii_case("AUTO_REFRESH")
11957                                        || next.text.eq_ignore_ascii_case("LOCATION")
11958                                        || next.text.eq_ignore_ascii_case("PARTITION")
11959                                        || next.text.eq_ignore_ascii_case("WAREHOUSE")
11960                                    {
11961                                        // Consume the trailing slash before the keyword
11962                                        self.skip();
11963                                        path.push('/');
11964                                        break;
11965                                    }
11966                                }
11967                                self.skip();
11968                                path.push('/');
11969                                if self.is_identifier_token()
11970                                    || self.is_safe_keyword_as_identifier()
11971                                {
11972                                    path.push_str(&self.advance().text);
11973                                }
11974                            }
11975                            path
11976                        } else if self.check(TokenType::Var) && self.peek().text.starts_with('@') {
11977                            // Stage path tokenized as Var (e.g., @s2/logs/)
11978                            // When @ is followed by alphanumeric, tokenizer creates a Var token
11979                            let mut path = self.advance().text;
11980                            // Parse path segments, but stop before Snowflake option keywords
11981                            while self.check(TokenType::Slash) {
11982                                // Peek ahead to see if next identifier is a Snowflake option keyword
11983                                if self.current + 1 < self.tokens.len() {
11984                                    let next = &self.tokens[self.current + 1];
11985                                    if next.text.eq_ignore_ascii_case("FILE_FORMAT")
11986                                        || next.text.eq_ignore_ascii_case("PARTITION_TYPE")
11987                                        || next.text.eq_ignore_ascii_case("AUTO_REFRESH")
11988                                        || next.text.eq_ignore_ascii_case("LOCATION")
11989                                        || next.text.eq_ignore_ascii_case("PARTITION")
11990                                        || next.text.eq_ignore_ascii_case("WAREHOUSE")
11991                                    {
11992                                        // Consume the trailing slash before the keyword
11993                                        self.skip();
11994                                        path.push('/');
11995                                        break;
11996                                    }
11997                                }
11998                                self.skip();
11999                                path.push('/');
12000                                if self.is_identifier_token()
12001                                    || self.is_safe_keyword_as_identifier()
12002                                {
12003                                    path.push_str(&self.advance().text);
12004                                }
12005                            }
12006                            path
12007                        } else if self.check(TokenType::Warehouse) {
12008                            self.advance().text
12009                        } else if self.is_identifier_token() || self.is_safe_keyword_as_identifier()
12010                        {
12011                            self.advance().text
12012                        } else {
12013                            // No valid value after =, retreat and let Hive parsing try
12014                            self.current = saved;
12015                            break;
12016                        };
12017                        all_with_properties.push((key, value));
12018                    } else if self.is_identifier_token()
12019                        || self.is_safe_keyword_as_identifier()
12020                        || self.check(TokenType::Warehouse)
12021                    {
12022                        // WAREHOUSE mywh (without =)
12023                        let value = self.advance().text;
12024                        all_with_properties.push((key, value));
12025                    } else {
12026                        // Not a Snowflake-style option (e.g., Hive LOCATION 'path' without =)
12027                        // Retreat and let Hive parsing try
12028                        self.current = saved;
12029                        break;
12030                    }
12031                } else {
12032                    break;
12033                }
12034            }
12035        }
12036
12037        // Parse MySQL table options: ENGINE=val, AUTO_INCREMENT=val, DEFAULT CHARSET=val, etc.
12038        let mysql_table_options = if is_clickhouse {
12039            Vec::new()
12040        } else {
12041            self.parse_mysql_table_options()
12042        };
12043
12044        // Parse StarRocks ROLLUP property: ROLLUP (r1(col1, col2), r2(col1))
12045        let rollup = if self.match_token(TokenType::Rollup) {
12046            self.expect(TokenType::LParen)?;
12047            let mut indices = Vec::new();
12048            loop {
12049                let name = self.expect_identifier_or_keyword_with_quoted()?;
12050                let cols = if self.match_token(TokenType::LParen) {
12051                    let mut col_list = Vec::new();
12052                    loop {
12053                        col_list.push(self.expect_identifier_or_keyword_with_quoted()?);
12054                        if !self.match_token(TokenType::Comma) {
12055                            break;
12056                        }
12057                    }
12058                    self.expect(TokenType::RParen)?;
12059                    col_list
12060                } else {
12061                    Vec::new()
12062                };
12063                indices.push(crate::expressions::RollupIndex {
12064                    name,
12065                    expressions: cols,
12066                });
12067                if !self.match_token(TokenType::Comma) {
12068                    break;
12069                }
12070            }
12071            self.expect(TokenType::RParen)?;
12072            Some(crate::expressions::RollupProperty {
12073                expressions: indices,
12074            })
12075        } else {
12076            None
12077        };
12078
12079        // Parse Hive table properties: ROW FORMAT, STORED AS/BY, LOCATION, TBLPROPERTIES
12080        let hive_properties = self.parse_hive_table_properties()?;
12081        let is_teradata = matches!(
12082            self.config.dialect,
12083            Some(crate::dialects::DialectType::Teradata)
12084        );
12085
12086        // Handle ON COMMIT PRESERVE ROWS or ON COMMIT DELETE ROWS
12087        // Also handle TSQL ON filegroup or ON filegroup (partition_column)
12088        let (mut on_commit, on_property) = if is_teradata {
12089            (None, None)
12090        } else if self.match_token(TokenType::On) {
12091            if self.match_token(TokenType::Commit) {
12092                if self.match_keywords(&[TokenType::Preserve, TokenType::Rows]) {
12093                    (Some(OnCommit::PreserveRows), None)
12094                } else if self.match_keywords(&[TokenType::Delete, TokenType::Rows]) {
12095                    (Some(OnCommit::DeleteRows), None)
12096                } else {
12097                    return Err(
12098                        self.parse_error("Expected PRESERVE ROWS or DELETE ROWS after ON COMMIT")
12099                    );
12100                }
12101            } else {
12102                // TSQL: ON filegroup or ON filegroup (partition_column)
12103                // Parse filegroup name as schema which allows filegroup(column) syntax
12104                let filegroup = self.parse_schema_identifier()?;
12105                (
12106                    None,
12107                    Some(OnProperty {
12108                        this: Box::new(filegroup),
12109                    }),
12110                )
12111            }
12112        } else {
12113            (None, None)
12114        };
12115
12116        // Parse table properties like DEFAULT COLLATE (BigQuery)
12117        let mut table_properties = hive_properties;
12118
12119        // If COMMENT was found before WITH, add it to table_properties as SchemaCommentProperty
12120        if let Some(comment_text) = pre_with_comment {
12121            table_properties.push(Expression::SchemaCommentProperty(Box::new(
12122                SchemaCommentProperty {
12123                    this: Box::new(Expression::Literal(Literal::String(comment_text))),
12124                },
12125            )));
12126        }
12127
12128        if self.match_token(TokenType::Default) && self.match_token(TokenType::Collate) {
12129            let collation = self.parse_primary()?;
12130            table_properties.push(Expression::CollateProperty(Box::new(CollateProperty {
12131                this: Box::new(collation),
12132                default: Some(Box::new(Expression::Boolean(BooleanLiteral {
12133                    value: true,
12134                }))),
12135            })));
12136        }
12137
12138        // BigQuery: OPTIONS (key=value, ...) on table - comes after column definitions
12139        if matches!(
12140            self.config.dialect,
12141            Some(crate::dialects::DialectType::BigQuery)
12142        ) {
12143            if let Some(options_property) = self.parse_bigquery_options_property()? {
12144                table_properties.push(options_property);
12145            }
12146        } else if self.match_identifier("OPTIONS") {
12147            let options = self.parse_options_list()?;
12148            table_properties.push(Expression::Properties(Box::new(Properties {
12149                expressions: options,
12150            })));
12151        }
12152
12153        // Doris/StarRocks: PROPERTIES ('key'='value', ...) - comes after column definitions
12154        let is_doris_starrocks = matches!(
12155            self.config.dialect,
12156            Some(crate::dialects::DialectType::Doris)
12157                | Some(crate::dialects::DialectType::StarRocks)
12158        );
12159        if is_doris_starrocks && self.match_identifier("PROPERTIES") {
12160            // Use parse_options_list which handles 'key'='value' format
12161            let props = self.parse_options_list()?;
12162            if !props.is_empty() {
12163                table_properties.push(Expression::Properties(Box::new(Properties {
12164                    expressions: props,
12165                })));
12166            }
12167        }
12168
12169        // Redshift: Parse DISTKEY, SORTKEY, DISTSTYLE, BACKUP after column definitions
12170        // These can appear in any order and multiple times
12171        loop {
12172            if self.match_identifier("DISTKEY") {
12173                // DISTKEY(column)
12174                if let Some(distkey) = self.parse_distkey()? {
12175                    table_properties.push(distkey);
12176                }
12177            } else if self.match_text_seq(&["COMPOUND", "SORTKEY"]) {
12178                // COMPOUND SORTKEY(col1, col2, ...)
12179                if let Some(sortkey) = self.parse_sortkey()? {
12180                    // Set compound flag
12181                    if let Expression::SortKeyProperty(mut skp) = sortkey {
12182                        skp.compound = Some(Box::new(Expression::Boolean(BooleanLiteral {
12183                            value: true,
12184                        })));
12185                        table_properties.push(Expression::SortKeyProperty(skp));
12186                    }
12187                }
12188            } else if self.match_identifier("SORTKEY") {
12189                // SORTKEY(col1, col2, ...)
12190                if let Some(sortkey) = self.parse_sortkey()? {
12191                    table_properties.push(sortkey);
12192                }
12193            } else if self.match_identifier("DISTSTYLE") {
12194                // DISTSTYLE ALL|EVEN|AUTO|KEY
12195                if self.match_texts(&["ALL", "EVEN", "AUTO", "KEY"]) {
12196                    let style = self.previous().text.to_ascii_uppercase();
12197                    table_properties.push(Expression::DistStyleProperty(Box::new(
12198                        DistStyleProperty {
12199                            this: Box::new(Expression::Var(Box::new(Var { this: style }))),
12200                        },
12201                    )));
12202                }
12203            } else if self.match_identifier("BACKUP") {
12204                // BACKUP YES|NO
12205                if self.match_texts(&["YES", "NO"]) {
12206                    let value = self.previous().text.to_ascii_uppercase();
12207                    table_properties.push(Expression::BackupProperty(Box::new(BackupProperty {
12208                        this: Box::new(Expression::Var(Box::new(Var { this: value }))),
12209                    })));
12210                }
12211            } else {
12212                break;
12213            }
12214        }
12215
12216        // Teradata: PRIMARY/UNIQUE/INDEX and PARTITION BY clauses after columns
12217        if is_teradata {
12218            loop {
12219                // Consume optional comma separator between index specs (only if followed by an index keyword)
12220                if self.check(TokenType::Comma) {
12221                    let saved_comma = self.current;
12222                    self.skip(); // consume comma
12223                    let is_index_keyword = self.check(TokenType::Unique)
12224                        || self.check(TokenType::PrimaryKey)
12225                        || self.check(TokenType::Index)
12226                        || self.check(TokenType::No);
12227                    if !is_index_keyword {
12228                        self.current = saved_comma; // retreat
12229                    }
12230                }
12231                if self.match_token(TokenType::Unique) {
12232                    let primary = self.match_token(TokenType::PrimaryKey);
12233                    let amp = self.match_identifier("AMP");
12234                    self.match_token(TokenType::Index);
12235                    let params = if self.match_token(TokenType::LParen) {
12236                        let cols = self.parse_identifier_list()?;
12237                        self.expect(TokenType::RParen)?;
12238                        cols.into_iter()
12239                            .map(|id| {
12240                                Expression::boxed_column(Column {
12241                                    name: id,
12242                                    table: None,
12243                                    join_mark: false,
12244                                    trailing_comments: Vec::new(),
12245                                    span: None,
12246                                    inferred_type: None,
12247                                })
12248                            })
12249                            .collect()
12250                    } else {
12251                        Vec::new()
12252                    };
12253                    table_properties.push(Expression::Index(Box::new(Index {
12254                        this: None,
12255                        table: None,
12256                        unique: true,
12257                        primary: if primary {
12258                            Some(Box::new(Expression::Boolean(BooleanLiteral {
12259                                value: true,
12260                            })))
12261                        } else {
12262                            None
12263                        },
12264                        amp: if amp {
12265                            Some(Box::new(Expression::Boolean(BooleanLiteral {
12266                                value: true,
12267                            })))
12268                        } else {
12269                            None
12270                        },
12271                        params,
12272                    })));
12273                    continue;
12274                }
12275                if self.match_token(TokenType::PrimaryKey) {
12276                    let amp = self.match_identifier("AMP");
12277                    self.match_token(TokenType::Index);
12278                    let params = if self.match_token(TokenType::LParen) {
12279                        let cols = self.parse_identifier_list()?;
12280                        self.expect(TokenType::RParen)?;
12281                        cols.into_iter()
12282                            .map(|id| {
12283                                Expression::boxed_column(Column {
12284                                    name: id,
12285                                    table: None,
12286                                    join_mark: false,
12287                                    trailing_comments: Vec::new(),
12288                                    span: None,
12289                                    inferred_type: None,
12290                                })
12291                            })
12292                            .collect()
12293                    } else {
12294                        Vec::new()
12295                    };
12296                    table_properties.push(Expression::Index(Box::new(Index {
12297                        this: None,
12298                        table: None,
12299                        unique: false,
12300                        primary: Some(Box::new(Expression::Boolean(BooleanLiteral {
12301                            value: true,
12302                        }))),
12303                        amp: if amp {
12304                            Some(Box::new(Expression::Boolean(BooleanLiteral {
12305                                value: true,
12306                            })))
12307                        } else {
12308                            None
12309                        },
12310                        params,
12311                    })));
12312                    continue;
12313                }
12314                if self.match_token(TokenType::Index) {
12315                    let params = if self.match_token(TokenType::LParen) {
12316                        let cols = self.parse_identifier_list()?;
12317                        self.expect(TokenType::RParen)?;
12318                        cols.into_iter()
12319                            .map(|id| {
12320                                Expression::boxed_column(Column {
12321                                    name: id,
12322                                    table: None,
12323                                    join_mark: false,
12324                                    trailing_comments: Vec::new(),
12325                                    span: None,
12326                                    inferred_type: None,
12327                                })
12328                            })
12329                            .collect()
12330                    } else {
12331                        Vec::new()
12332                    };
12333                    table_properties.push(Expression::Index(Box::new(Index {
12334                        this: None,
12335                        table: None,
12336                        unique: false,
12337                        primary: None,
12338                        amp: None,
12339                        params,
12340                    })));
12341                    continue;
12342                }
12343                if self.match_keywords(&[TokenType::Partition, TokenType::By]) {
12344                    let expr = self.parse_primary()?;
12345                    table_properties.push(Expression::PartitionedByProperty(Box::new(
12346                        PartitionedByProperty {
12347                            this: Box::new(expr),
12348                        },
12349                    )));
12350                    continue;
12351                }
12352                break;
12353            }
12354
12355            if on_commit.is_none()
12356                && self.check(TokenType::On)
12357                && self.check_next(TokenType::Commit)
12358            {
12359                self.skip(); // ON
12360                self.skip(); // COMMIT
12361                if self.match_keywords(&[TokenType::Preserve, TokenType::Rows]) {
12362                    on_commit = Some(OnCommit::PreserveRows);
12363                } else if self.match_keywords(&[TokenType::Delete, TokenType::Rows]) {
12364                    on_commit = Some(OnCommit::DeleteRows);
12365                } else {
12366                    return Err(
12367                        self.parse_error("Expected PRESERVE ROWS or DELETE ROWS after ON COMMIT")
12368                    );
12369                }
12370            }
12371        }
12372
12373        // ClickHouse: table properties after column definitions
12374        if is_clickhouse {
12375            self.parse_clickhouse_table_properties(&mut table_properties)?;
12376        }
12377
12378        // ClickHouse: EMPTY AS SELECT
12379        if matches!(
12380            self.config.dialect,
12381            Some(crate::dialects::DialectType::ClickHouse)
12382        ) && self.match_identifier("EMPTY")
12383        {
12384            table_properties.push(Expression::Var(Box::new(Var {
12385                this: "EMPTY".to_string(),
12386            })));
12387        }
12388
12389        // Handle AS SELECT after columns/WITH (CTAS with column definitions)
12390        // When there are no column definitions, AS comes after PARTITION BY/CLUSTER BY/OPTIONS
12391        let as_select = if !no_column_defs && self.match_token(TokenType::As) {
12392            Some(self.parse_statement()?)
12393        } else {
12394            None
12395        };
12396
12397        if is_clickhouse && as_select.is_some() {
12398            self.parse_clickhouse_table_properties(&mut table_properties)?;
12399        }
12400
12401        // Parse PARTITION BY RANGE/LIST/HASH(columns) for regular CREATE TABLE
12402        let is_bigquery = matches!(
12403            self.config.dialect,
12404            Some(crate::dialects::DialectType::BigQuery)
12405        );
12406        if !is_teradata && (self.check(TokenType::Partition) || self.check(TokenType::PartitionBy))
12407        {
12408            let parsed_bigquery_partition = if is_bigquery {
12409                if let Some(partition_property) = self.parse_bigquery_partition_by_property()? {
12410                    table_properties.push(partition_property);
12411                    true
12412                } else {
12413                    false
12414                }
12415            } else {
12416                false
12417            };
12418
12419            if !parsed_bigquery_partition {
12420                let saved = self.current;
12421                let is_partition_by = if self.match_token(TokenType::PartitionBy) {
12422                    true
12423                } else if self.match_token(TokenType::Partition) {
12424                    self.match_token(TokenType::By)
12425                } else {
12426                    false
12427                };
12428                if is_partition_by {
12429                    let partition_kind = if self.check(TokenType::Range) {
12430                        self.skip();
12431                        Some("RANGE".to_string())
12432                    } else if self.check(TokenType::List) {
12433                        self.skip();
12434                        Some("LIST".to_string())
12435                    } else if (self.check(TokenType::Identifier) || self.check(TokenType::Var))
12436                        && self.check_next(TokenType::LParen)
12437                    {
12438                        // Only treat identifier as partition method (like HASH) if followed by (
12439                        Some(self.advance().text.to_ascii_uppercase())
12440                    } else {
12441                        // No explicit partition method (RANGE/LIST/HASH), just PARTITION BY (cols)
12442                        None
12443                    };
12444
12445                    // StarRocks/Doris: PARTITION BY func(), col (bare expressions without RANGE/LIST)
12446                    // When the partition_kind was consumed as an identifier that's actually a function call
12447                    // and the content after the parenthesized args includes a comma, it's a bare expression list
12448                    if is_doris_starrocks
12449                        && partition_kind.is_some()
12450                        && !matches!(
12451                            partition_kind.as_deref(),
12452                            Some("RANGE") | Some("LIST") | Some("HASH") | Some("KEY")
12453                        )
12454                    {
12455                        // Backtrack: re-parse as bare PARTITION BY with comma-separated expressions
12456                        let func_name = partition_kind.unwrap();
12457                        let mut raw_sql = format!("PARTITION BY {}", func_name);
12458                        // Helper closure for consuming parenthesized content with proper spacing
12459                        fn consume_parens(parser: &mut Parser, raw_sql: &mut String) {
12460                            if !parser.check(TokenType::LParen) {
12461                                return;
12462                            }
12463                            parser.advance();
12464                            raw_sql.push('(');
12465                            let mut depth = 1;
12466                            let mut last_type: Option<TokenType> = None;
12467                            while !parser.is_at_end() && depth > 0 {
12468                                let tok = parser.advance();
12469                                if tok.token_type == TokenType::LParen {
12470                                    depth += 1;
12471                                } else if tok.token_type == TokenType::RParen {
12472                                    depth -= 1;
12473                                    if depth == 0 {
12474                                        break;
12475                                    }
12476                                }
12477                                // Add space after commas
12478                                if matches!(last_type, Some(TokenType::Comma)) {
12479                                    raw_sql.push(' ');
12480                                }
12481                                if tok.token_type == TokenType::String {
12482                                    raw_sql.push('\'');
12483                                    raw_sql.push_str(&tok.text);
12484                                    raw_sql.push('\'');
12485                                } else {
12486                                    raw_sql.push_str(&tok.text);
12487                                }
12488                                last_type = Some(tok.token_type.clone());
12489                            }
12490                            raw_sql.push(')');
12491                        }
12492                        consume_parens(self, &mut raw_sql);
12493                        // Consume more comma-separated expressions
12494                        while self.match_token(TokenType::Comma) {
12495                            raw_sql.push_str(", ");
12496                            let tok = self.advance();
12497                            raw_sql.push_str(&tok.text);
12498                            consume_parens(self, &mut raw_sql);
12499                        }
12500                        table_properties.push(Expression::Raw(Raw { sql: raw_sql }));
12501                    } else
12502                    // For Doris/StarRocks/MySQL RANGE/LIST, use structured parsing
12503                    if (is_doris_starrocks
12504                        || matches!(
12505                            self.config.dialect,
12506                            Some(crate::dialects::DialectType::MySQL)
12507                                | Some(crate::dialects::DialectType::SingleStore)
12508                                | Some(crate::dialects::DialectType::TiDB)
12509                        ))
12510                        && matches!(partition_kind.as_deref(), Some("RANGE") | Some("LIST"))
12511                    {
12512                        let partition_expr = self.parse_doris_partition_by_range_or_list(
12513                            partition_kind
12514                                .as_ref()
12515                                .map(|s| s.as_str())
12516                                .unwrap_or("RANGE"),
12517                        )?;
12518                        table_properties.push(partition_expr);
12519                    } else {
12520                        // Generic raw SQL parsing for other dialects
12521                        let no_partition_kind = partition_kind.is_none();
12522                        let mut raw_sql = match partition_kind {
12523                            Some(kind) => format!("PARTITION BY {}", kind),
12524                            None => "PARTITION BY ".to_string(),
12525                        };
12526                        if self.check(TokenType::LParen) {
12527                            self.skip();
12528                            raw_sql.push('(');
12529                            let mut depth = 1;
12530                            let mut last_tok_type: Option<TokenType> = None;
12531                            while !self.is_at_end() && depth > 0 {
12532                                let tok = self.advance();
12533                                if tok.token_type == TokenType::LParen {
12534                                    depth += 1;
12535                                } else if tok.token_type == TokenType::RParen {
12536                                    depth -= 1;
12537                                    if depth == 0 {
12538                                        break;
12539                                    }
12540                                }
12541                                // Add space before token if needed for proper formatting
12542                                let needs_space = match (&last_tok_type, &tok.token_type) {
12543                                    // Add space after comma
12544                                    (Some(TokenType::Comma), _) => true,
12545                                    // Add space after identifiers/keywords before other identifiers/keywords
12546                                    (Some(TokenType::Identifier), TokenType::Identifier) => true,
12547                                    _ => false,
12548                                };
12549                                if needs_space {
12550                                    raw_sql.push(' ');
12551                                }
12552                                // Handle string literals - preserve quotes
12553                                if tok.token_type == TokenType::String {
12554                                    raw_sql.push('\'');
12555                                    raw_sql.push_str(&tok.text);
12556                                    raw_sql.push('\'');
12557                                } else {
12558                                    raw_sql.push_str(&tok.text);
12559                                }
12560                                last_tok_type = Some(tok.token_type.clone());
12561                            }
12562                            raw_sql.push(')');
12563                        } else if no_partition_kind {
12564                            // Bare PARTITION BY expression list without a partition method
12565                            let mut first = true;
12566                            while !self.is_at_end()
12567                                && !self.check(TokenType::Cluster)
12568                                && !self.check(TokenType::As)
12569                                && !self.check(TokenType::Semicolon)
12570                                && !self.check(TokenType::RParen)
12571                                && !self.check_identifier("OPTIONS")
12572                            {
12573                                if !first {
12574                                    raw_sql.push_str(", ");
12575                                }
12576                                first = false;
12577                                let tok = self.advance();
12578                                raw_sql.push_str(&tok.text);
12579                                // Handle function calls: PARTITION BY DATE(col)
12580                                if self.check(TokenType::LParen) {
12581                                    self.skip();
12582                                    raw_sql.push('(');
12583                                    let mut depth = 1;
12584                                    while !self.is_at_end() && depth > 0 {
12585                                        let t = self.advance();
12586                                        if t.token_type == TokenType::LParen {
12587                                            depth += 1;
12588                                        } else if t.token_type == TokenType::RParen {
12589                                            depth -= 1;
12590                                            if depth == 0 {
12591                                                break;
12592                                            }
12593                                        }
12594                                        raw_sql.push_str(&t.text);
12595                                    }
12596                                    raw_sql.push(')');
12597                                }
12598                                if !self.match_token(TokenType::Comma) {
12599                                    break;
12600                                }
12601                            }
12602                        }
12603                        table_properties.push(Expression::Raw(Raw { sql: raw_sql }));
12604                    }
12605                } else {
12606                    self.current = saved;
12607                }
12608            }
12609        }
12610
12611        // Parse CLUSTER BY (BigQuery) after PARTITION BY
12612        if is_bigquery {
12613            if let Some(cluster_property) = self.parse_bigquery_cluster_by_property()? {
12614                table_properties.push(cluster_property);
12615            }
12616        } else if self.match_keywords(&[TokenType::Cluster, TokenType::By]) {
12617            let mut cluster_names = Vec::new();
12618            loop {
12619                let name = self.expect_identifier_or_keyword()?;
12620                cluster_names.push(name);
12621                if !self.match_token(TokenType::Comma) {
12622                    break;
12623                }
12624            }
12625            table_properties.push(Expression::Raw(Raw {
12626                sql: format!("CLUSTER BY {}", cluster_names.join(", ")),
12627            }));
12628        }
12629
12630        // No-column-defs path: OPTIONS and AS SELECT come after PARTITION BY / CLUSTER BY
12631        if no_column_defs {
12632            if matches!(
12633                self.config.dialect,
12634                Some(crate::dialects::DialectType::BigQuery)
12635            ) {
12636                if let Some(options_property) = self.parse_bigquery_options_property()? {
12637                    table_properties.push(options_property);
12638                }
12639            } else if self.match_identifier("OPTIONS") {
12640                let options = self.parse_options_list()?;
12641                table_properties.push(Expression::Properties(Box::new(Properties {
12642                    expressions: options,
12643                })));
12644            }
12645        }
12646
12647        let as_select = if no_column_defs && self.match_token(TokenType::As) {
12648            Some(self.parse_statement()?)
12649        } else {
12650            as_select
12651        };
12652
12653        // For EXTERNAL tables, parse additional Snowflake options that may come after PARTITION BY
12654        // (location=@s2/logs/, partition_type = user_specified, file_format = (...), etc.)
12655        if is_special_modifier {
12656            while !self.is_at_end()
12657                && !self.check(TokenType::As)
12658                && !self.check(TokenType::Semicolon)
12659            {
12660                let is_snowflake_option = self.check(TokenType::Warehouse)
12661                    || self.check_identifier("TARGET_LAG")
12662                    || self.check_identifier("CATALOG")
12663                    || self.check_identifier("EXTERNAL_VOLUME")
12664                    || self.check_identifier("BASE_LOCATION")
12665                    || self.check_identifier("REFRESH_MODE")
12666                    || self.check_identifier("INITIALIZE")
12667                    || self.check_identifier("DATA_RETENTION_TIME_IN_DAYS")
12668                    || self.check_identifier("LOCATION")
12669                    || self.check_identifier("PARTITION_TYPE")
12670                    || self.check_identifier("FILE_FORMAT")
12671                    || self.check_identifier("AUTO_REFRESH");
12672                if is_snowflake_option {
12673                    let key = self.advance().text;
12674                    if self.match_token(TokenType::Eq) {
12675                        let value = if self.check(TokenType::LParen) {
12676                            // Parenthesized option list
12677                            self.skip();
12678                            let mut options = String::from("(");
12679                            let mut depth = 1;
12680                            while !self.is_at_end() && depth > 0 {
12681                                let tok = self.advance();
12682                                if tok.token_type == TokenType::LParen {
12683                                    depth += 1;
12684                                } else if tok.token_type == TokenType::RParen {
12685                                    depth -= 1;
12686                                }
12687                                if !options.ends_with('(')
12688                                    && !options.ends_with(' ')
12689                                    && tok.token_type != TokenType::RParen
12690                                {
12691                                    options.push(' ');
12692                                }
12693                                options.push_str(&tok.text);
12694                            }
12695                            options
12696                        } else if self.check(TokenType::String) {
12697                            let v = format!("'{}'", self.peek().text);
12698                            self.skip();
12699                            v
12700                        } else if self.check(TokenType::DAt) {
12701                            // Stage path like @s1/logs/
12702                            self.skip();
12703                            let mut path = String::from("@");
12704                            if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
12705                                path.push_str(&self.advance().text);
12706                            }
12707                            while self.check(TokenType::Slash) {
12708                                if self.current + 1 < self.tokens.len() {
12709                                    let next = &self.tokens[self.current + 1];
12710                                    if next.text.eq_ignore_ascii_case("FILE_FORMAT")
12711                                        || next.text.eq_ignore_ascii_case("PARTITION_TYPE")
12712                                        || next.text.eq_ignore_ascii_case("AUTO_REFRESH")
12713                                        || next.text.eq_ignore_ascii_case("LOCATION")
12714                                        || next.text.eq_ignore_ascii_case("PARTITION")
12715                                        || next.text.eq_ignore_ascii_case("WAREHOUSE")
12716                                    {
12717                                        self.skip();
12718                                        path.push('/');
12719                                        break;
12720                                    }
12721                                }
12722                                self.skip();
12723                                path.push('/');
12724                                if self.is_identifier_token()
12725                                    || self.is_safe_keyword_as_identifier()
12726                                {
12727                                    path.push_str(&self.advance().text);
12728                                }
12729                            }
12730                            path
12731                        } else if self.check(TokenType::Var) && self.peek().text.starts_with('@') {
12732                            let mut path = self.advance().text;
12733                            while self.check(TokenType::Slash) {
12734                                if self.current + 1 < self.tokens.len() {
12735                                    let next = &self.tokens[self.current + 1];
12736                                    if next.text.eq_ignore_ascii_case("FILE_FORMAT")
12737                                        || next.text.eq_ignore_ascii_case("PARTITION_TYPE")
12738                                        || next.text.eq_ignore_ascii_case("AUTO_REFRESH")
12739                                        || next.text.eq_ignore_ascii_case("LOCATION")
12740                                        || next.text.eq_ignore_ascii_case("PARTITION")
12741                                        || next.text.eq_ignore_ascii_case("WAREHOUSE")
12742                                    {
12743                                        self.skip();
12744                                        path.push('/');
12745                                        break;
12746                                    }
12747                                }
12748                                self.skip();
12749                                path.push('/');
12750                                if self.is_identifier_token()
12751                                    || self.is_safe_keyword_as_identifier()
12752                                {
12753                                    path.push_str(&self.advance().text);
12754                                }
12755                            }
12756                            path
12757                        } else if self.check(TokenType::Warehouse) {
12758                            self.advance().text
12759                        } else if self.is_identifier_token() || self.is_safe_keyword_as_identifier()
12760                        {
12761                            self.advance().text
12762                        } else {
12763                            break;
12764                        };
12765                        all_with_properties.push((key, value));
12766                    } else if self.is_identifier_token()
12767                        || self.is_safe_keyword_as_identifier()
12768                        || self.check(TokenType::Warehouse)
12769                    {
12770                        let value = self.advance().text;
12771                        all_with_properties.push((key, value));
12772                    }
12773                } else {
12774                    break;
12775                }
12776            }
12777        }
12778
12779        // Parse TSQL table-level WITH(SYSTEM_VERSIONING=ON(...)) after columns
12780        // This is different from the earlier WITH properties parsing.
12781        // TSQL uses WITH(...) after columns for system versioning.
12782        let post_table_properties = self.parse_post_table_properties()?;
12783
12784        // PostgreSQL: INHERITS (parent1, parent2, ...)
12785        let inherits = if self.match_identifier("INHERITS") {
12786            self.expect(TokenType::LParen)?;
12787            let mut parents = Vec::new();
12788            loop {
12789                parents.push(self.parse_table_ref()?);
12790                if !self.match_token(TokenType::Comma) {
12791                    break;
12792                }
12793            }
12794            self.expect(TokenType::RParen)?;
12795            parents
12796        } else {
12797            Vec::new()
12798        };
12799
12800        Ok(Expression::CreateTable(Box::new(CreateTable {
12801            name,
12802            on_cluster,
12803            columns,
12804            constraints,
12805            if_not_exists,
12806            temporary,
12807            or_replace,
12808            table_modifier: table_modifier.map(|s| s.to_string()),
12809            as_select,
12810            as_select_parenthesized: false,
12811            on_commit,
12812            clone_source: None,
12813            clone_at_clause: None,
12814            shallow_clone: false,
12815            is_copy: false,
12816            leading_comments,
12817            with_properties: all_with_properties,
12818            teradata_post_name_options: teradata_post_name_options.clone(),
12819            with_data: None,
12820            with_statistics: None,
12821            teradata_indexes: Vec::new(),
12822            with_cte: None,
12823            properties: table_properties,
12824            partition_of: None,
12825            post_table_properties,
12826            mysql_table_options,
12827            inherits,
12828            on_property,
12829            copy_grants,
12830            using_template: None,
12831            rollup,
12832        })))
12833    }
12834
12835    /// Parse CREATE TABLE ... PARTITION OF parent_table [(cols)] [FOR VALUES spec | DEFAULT] [PARTITION BY ...]
12836    fn parse_create_table_partition_of(
12837        &mut self,
12838        name: TableRef,
12839        if_not_exists: bool,
12840        temporary: bool,
12841        or_replace: bool,
12842        table_modifier: Option<&str>,
12843        leading_comments: Vec<String>,
12844    ) -> Result<Expression> {
12845        // Parse parent table name
12846        let parent_table = self.parse_table_ref()?;
12847
12848        // Optionally parse column constraints in parens: (unitsales DEFAULT 0) or (CONSTRAINT ...)
12849        // This must come before FOR VALUES or DEFAULT. We distinguish from other uses
12850        // by checking if the first token after LParen is CONSTRAINT or an identifier
12851        // that is not a string literal.
12852        let (columns, constraints) = if self.check(TokenType::LParen) {
12853            // Peek ahead: current is LParen, current+1 is first token inside parens
12854            let first_inside = self.current + 1;
12855            // Check if this is a partition column specification: (colname DEFAULT value)
12856            // Column names tokenize as Var (unquoted) or QuotedIdentifier (quoted)
12857            let is_column_defs = first_inside < self.tokens.len()
12858                && (self.tokens[first_inside].token_type == TokenType::Constraint
12859                    || ((self.tokens[first_inside].token_type == TokenType::Var
12860                        || self.tokens[first_inside].token_type == TokenType::QuotedIdentifier
12861                        || self.tokens[first_inside].token_type == TokenType::Identifier)
12862                        && first_inside + 1 < self.tokens.len()
12863                        && self.tokens[first_inside + 1].token_type == TokenType::Default));
12864
12865            if is_column_defs {
12866                self.skip(); // consume LParen
12867                                // Use special parsing for partition column specs - they don't have data types,
12868                                // just column names with constraint overrides like DEFAULT
12869                let (cols, constrs) = self.parse_partition_column_specs()?;
12870                self.expect(TokenType::RParen)?;
12871                (cols, constrs)
12872            } else {
12873                (Vec::new(), Vec::new())
12874            }
12875        } else {
12876            (Vec::new(), Vec::new())
12877        };
12878
12879        // Parse DEFAULT or FOR VALUES spec
12880        let partition_bound: Expression = if self.match_token(TokenType::Default) {
12881            // DEFAULT partition
12882            Expression::Var(Box::new(Var {
12883                this: "DEFAULT".to_string(),
12884            }))
12885        } else if self.match_token(TokenType::For) {
12886            // FOR VALUES ...
12887            self.expect(TokenType::Values)?;
12888            self.parse_partition_bound_spec()?
12889        } else {
12890            // Neither DEFAULT nor FOR VALUES - could be an error
12891            // but we'll be lenient and just create a DEFAULT
12892            Expression::Var(Box::new(Var {
12893                this: "DEFAULT".to_string(),
12894            }))
12895        };
12896
12897        let partition_of_expr =
12898            Expression::PartitionedOfProperty(Box::new(PartitionedOfProperty {
12899                this: Box::new(Expression::Table(Box::new(parent_table))),
12900                expression: Box::new(partition_bound),
12901            }));
12902
12903        // Optionally parse trailing PARTITION BY RANGE/LIST/HASH(columns)
12904        let mut table_properties: Vec<Expression> = Vec::new();
12905        if self.match_token(TokenType::Partition) || self.match_token(TokenType::PartitionBy) {
12906            // Could be PARTITION BY or just PartitionBy token
12907            if self.previous().token_type == TokenType::Partition {
12908                self.expect(TokenType::By)?;
12909            }
12910            // Parse RANGE/LIST/HASH(columns)
12911            let partition_kind = if self.check(TokenType::Identifier) || self.check(TokenType::Var)
12912            {
12913                let kind_text = self.advance().text.to_ascii_uppercase();
12914                kind_text
12915            } else if self.check(TokenType::Range) {
12916                self.skip();
12917                "RANGE".to_string()
12918            } else if self.check(TokenType::List) {
12919                self.skip();
12920                "LIST".to_string()
12921            } else {
12922                "RANGE".to_string()
12923            };
12924            // Parse (columns)
12925            let mut raw_sql = format!("PARTITION BY {}", partition_kind);
12926            if self.check(TokenType::LParen) {
12927                self.skip(); // consume LParen
12928                raw_sql.push('(');
12929                let mut depth = 1;
12930                while !self.is_at_end() && depth > 0 {
12931                    let tok = self.advance();
12932                    if tok.token_type == TokenType::LParen {
12933                        depth += 1;
12934                    } else if tok.token_type == TokenType::RParen {
12935                        depth -= 1;
12936                        if depth == 0 {
12937                            break;
12938                        }
12939                    }
12940                    raw_sql.push_str(&tok.text);
12941                }
12942                raw_sql.push(')');
12943            }
12944            table_properties.push(Expression::Raw(Raw { sql: raw_sql }));
12945        }
12946
12947        Ok(Expression::CreateTable(Box::new(CreateTable {
12948            name,
12949            on_cluster: None,
12950            columns,
12951            constraints,
12952            if_not_exists,
12953            temporary,
12954            or_replace,
12955            table_modifier: table_modifier.map(|s| s.to_string()),
12956            as_select: None,
12957            as_select_parenthesized: false,
12958            on_commit: None,
12959            clone_source: None,
12960            clone_at_clause: None,
12961            shallow_clone: false,
12962            is_copy: false,
12963            leading_comments,
12964            with_properties: Vec::new(),
12965            teradata_post_name_options: Vec::new(),
12966            with_data: None,
12967            with_statistics: None,
12968            teradata_indexes: Vec::new(),
12969            with_cte: None,
12970            properties: table_properties,
12971            partition_of: Some(partition_of_expr),
12972            post_table_properties: Vec::new(),
12973            mysql_table_options: Vec::new(),
12974            inherits: Vec::new(),
12975            on_property: None,
12976            copy_grants: false,
12977            using_template: None,
12978            rollup: None,
12979        })))
12980    }
12981
12982    /// Parse partition bound spec for PARTITION OF: IN (...), FROM (...) TO (...), or WITH (MODULUS n, REMAINDER n)
12983    fn parse_partition_bound_spec(&mut self) -> Result<Expression> {
12984        if self.match_token(TokenType::In) {
12985            // IN (val, val, ...)
12986            self.expect(TokenType::LParen)?;
12987            let mut values = Vec::new();
12988            loop {
12989                let val = self.parse_expression()?;
12990                values.push(val);
12991                if !self.match_token(TokenType::Comma) {
12992                    break;
12993                }
12994            }
12995            self.expect(TokenType::RParen)?;
12996            // Use Tuple for multiple values (generator strips parens for partition bounds)
12997            let this_expr = if values.len() == 1 {
12998                values.into_iter().next().unwrap()
12999            } else {
13000                Expression::Tuple(Box::new(Tuple {
13001                    expressions: values,
13002                }))
13003            };
13004            Ok(Expression::PartitionBoundSpec(Box::new(
13005                PartitionBoundSpec {
13006                    this: Some(Box::new(this_expr)),
13007                    expression: None,
13008                    from_expressions: None,
13009                    to_expressions: None,
13010                },
13011            )))
13012        } else if self.match_token(TokenType::From) {
13013            // FROM (val, ...) TO (val, ...)
13014            self.expect(TokenType::LParen)?;
13015            let mut from_vals = Vec::new();
13016            loop {
13017                let val = self.parse_partition_bound_value()?;
13018                from_vals.push(val);
13019                if !self.match_token(TokenType::Comma) {
13020                    break;
13021                }
13022            }
13023            self.expect(TokenType::RParen)?;
13024
13025            self.expect(TokenType::To)?;
13026            self.expect(TokenType::LParen)?;
13027            let mut to_vals = Vec::new();
13028            loop {
13029                let val = self.parse_partition_bound_value()?;
13030                to_vals.push(val);
13031                if !self.match_token(TokenType::Comma) {
13032                    break;
13033                }
13034            }
13035            self.expect(TokenType::RParen)?;
13036
13037            let from_expr = if from_vals.len() == 1 {
13038                from_vals.into_iter().next().unwrap()
13039            } else {
13040                Expression::Tuple(Box::new(Tuple {
13041                    expressions: from_vals,
13042                }))
13043            };
13044            let to_expr = if to_vals.len() == 1 {
13045                to_vals.into_iter().next().unwrap()
13046            } else {
13047                Expression::Tuple(Box::new(Tuple {
13048                    expressions: to_vals,
13049                }))
13050            };
13051
13052            Ok(Expression::PartitionBoundSpec(Box::new(
13053                PartitionBoundSpec {
13054                    this: None,
13055                    expression: None,
13056                    from_expressions: Some(Box::new(from_expr)),
13057                    to_expressions: Some(Box::new(to_expr)),
13058                },
13059            )))
13060        } else if self.match_token(TokenType::With) {
13061            // WITH (MODULUS n, REMAINDER n)
13062            self.expect(TokenType::LParen)?;
13063            self.match_text_seq(&["MODULUS"]);
13064            let modulus = self.parse_expression()?;
13065            self.expect(TokenType::Comma)?;
13066            self.match_text_seq(&["REMAINDER"]);
13067            let remainder = self.parse_expression()?;
13068            self.expect(TokenType::RParen)?;
13069
13070            Ok(Expression::PartitionBoundSpec(Box::new(
13071                PartitionBoundSpec {
13072                    this: Some(Box::new(modulus)),
13073                    expression: Some(Box::new(remainder)),
13074                    from_expressions: None,
13075                    to_expressions: None,
13076                },
13077            )))
13078        } else {
13079            Err(self.parse_error("Expected IN, FROM, or WITH after FOR VALUES in PARTITION OF"))
13080        }
13081    }
13082
13083    /// Parse a single partition bound value (number, string, MINVALUE, MAXVALUE)
13084    fn parse_partition_bound_value(&mut self) -> Result<Expression> {
13085        if self.match_token(TokenType::Minvalue) {
13086            Ok(Expression::Var(Box::new(Var {
13087                this: "MINVALUE".to_string(),
13088            })))
13089        } else if self.match_token(TokenType::Maxvalue) {
13090            Ok(Expression::Var(Box::new(Var {
13091                this: "MAXVALUE".to_string(),
13092            })))
13093        } else {
13094            self.parse_expression()
13095        }
13096    }
13097
13098    /// Parse column specifications for PostgreSQL PARTITION OF syntax.
13099    /// Unlike regular column definitions, these don't have data types - just column names
13100    /// with constraint overrides like DEFAULT, NOT NULL, or table-level CONSTRAINT clauses.
13101    /// Example: (unitsales DEFAULT 0) or (CONSTRAINT check_date CHECK (logdate >= '2016-07-01'))
13102    fn parse_partition_column_specs(&mut self) -> Result<(Vec<ColumnDef>, Vec<TableConstraint>)> {
13103        let mut columns = Vec::new();
13104        let mut constraints = Vec::new();
13105
13106        loop {
13107            // Check for table-level constraint (CONSTRAINT name ...)
13108            if self.check(TokenType::Constraint) {
13109                constraints.push(self.parse_table_constraint()?);
13110            } else if self.check(TokenType::PrimaryKey)
13111                || self.check(TokenType::ForeignKey)
13112                || self.check(TokenType::Unique)
13113                || self.check(TokenType::Check)
13114                || self.check(TokenType::Exclude)
13115            {
13116                constraints.push(self.parse_table_constraint()?);
13117            } else {
13118                // Parse column name with optional constraints (no data type)
13119                columns.push(self.parse_partition_column_spec()?);
13120            }
13121
13122            if !self.match_token(TokenType::Comma) {
13123                break;
13124            }
13125            // ClickHouse allows a trailing comma before the closing ')'
13126            if matches!(
13127                self.config.dialect,
13128                Some(crate::dialects::DialectType::ClickHouse)
13129            ) && self.check(TokenType::RParen)
13130            {
13131                break;
13132            }
13133        }
13134
13135        Ok((columns, constraints))
13136    }
13137
13138    /// Parse a single partition column specification: column_name [DEFAULT value] [NOT NULL] [NULL] [WITH OPTIONS ...]
13139    fn parse_partition_column_spec(&mut self) -> Result<ColumnDef> {
13140        // Parse column name
13141        let name = self.expect_identifier_or_safe_keyword_with_quoted()?;
13142
13143        // Create column def with Unknown data type (data type comes from parent table)
13144        let mut col_def = ColumnDef::new(name.name.clone(), DataType::Unknown);
13145        col_def.name = name;
13146
13147        // Parse column constraints (no data type expected)
13148        loop {
13149            if self.match_token(TokenType::Default) {
13150                // DEFAULT value
13151                let default_val = self.parse_expression()?;
13152                col_def.default = Some(default_val.clone());
13153                col_def
13154                    .constraints
13155                    .push(ColumnConstraint::Default(default_val));
13156                col_def.constraint_order.push(ConstraintType::Default);
13157            } else if self.match_keywords(&[TokenType::Not, TokenType::Null]) {
13158                col_def.nullable = Some(false);
13159                col_def.constraint_order.push(ConstraintType::NotNull);
13160            } else if self.match_token(TokenType::Null) {
13161                col_def.nullable = Some(true);
13162                col_def.constraint_order.push(ConstraintType::Null);
13163            } else if self.match_token(TokenType::Constraint) {
13164                // Inline CONSTRAINT name ... for this column
13165                let constraint_name = self.expect_identifier_or_safe_keyword()?;
13166                if self.match_keywords(&[TokenType::Not, TokenType::Null]) {
13167                    col_def.nullable = Some(false);
13168                    col_def.not_null_constraint_name = Some(constraint_name);
13169                    col_def.constraint_order.push(ConstraintType::NotNull);
13170                } else if self.match_token(TokenType::Check) {
13171                    col_def.check_constraint_name = Some(constraint_name);
13172                    if self.match_token(TokenType::LParen) {
13173                        let check_expr = self.parse_expression()?;
13174                        self.expect(TokenType::RParen)?;
13175                        col_def
13176                            .constraints
13177                            .push(ColumnConstraint::Check(check_expr));
13178                    }
13179                    col_def.constraint_order.push(ConstraintType::Check);
13180                } else if self.match_token(TokenType::Default) {
13181                    let default_val = self.parse_expression()?;
13182                    col_def.default = Some(default_val.clone());
13183                    col_def
13184                        .constraints
13185                        .push(ColumnConstraint::Default(default_val));
13186                    col_def.constraint_order.push(ConstraintType::Default);
13187                }
13188            } else if self.match_text_seq(&["WITH", "OPTIONS"]) {
13189                // PostgreSQL: WITH OPTIONS allows specifying more options
13190                // For now, just skip this - it's rarely used
13191                break;
13192            } else {
13193                break;
13194            }
13195        }
13196
13197        Ok(col_def)
13198    }
13199
13200    /// Parse WITH properties for CREATE TABLE (e.g., WITH (FORMAT='parquet', x='2'))
13201    /// Returns a list of (key, value) pairs
13202    fn parse_with_properties(&mut self) -> Result<Vec<(String, String)>> {
13203        self.expect(TokenType::LParen)?;
13204        let mut properties = Vec::new();
13205
13206        loop {
13207            if self.check(TokenType::RParen) {
13208                break;
13209            }
13210
13211            // Parse property name (can be keywords like FORMAT, TABLE_FORMAT)
13212            let mut key = self.expect_identifier_or_keyword()?;
13213
13214            // Handle multi-word keys like "PARTITIONED BY" -> "PARTITIONED_BY"
13215            if key.eq_ignore_ascii_case("PARTITIONED") && self.check(TokenType::By) {
13216                self.skip(); // consume BY
13217                key = "PARTITIONED_BY".to_string();
13218            }
13219
13220            // Expect = or special case for PARTITIONED_BY=(...)
13221            self.expect(TokenType::Eq)?;
13222
13223            // Parse property value - can be string, identifier, or parenthesized expression
13224            let value = if self.check(TokenType::String) {
13225                // Store string with quotes to preserve format
13226                let val = format!("'{}'", self.peek().text);
13227                self.skip();
13228                val
13229            } else if self.match_token(TokenType::LParen) {
13230                // Handle PARTITIONED_BY=(x INT, y INT) or similar
13231                let mut depth = 1;
13232                let mut result = String::from("(");
13233                let mut need_space = false;
13234                while !self.is_at_end() && depth > 0 {
13235                    if self.check(TokenType::LParen) {
13236                        depth += 1;
13237                    } else if self.check(TokenType::RParen) {
13238                        depth -= 1;
13239                        if depth == 0 {
13240                            break;
13241                        }
13242                    }
13243                    let token = self.peek();
13244                    let text = &token.text;
13245                    let token_type = token.token_type;
13246
13247                    // Determine if we need a space before this token
13248                    let is_punctuation = matches!(
13249                        token_type,
13250                        TokenType::Comma | TokenType::LParen | TokenType::RParen
13251                    );
13252                    if need_space && !is_punctuation {
13253                        result.push(' ');
13254                    }
13255
13256                    result.push_str(text);
13257
13258                    // Determine if we need a space after this token
13259                    need_space = token_type == TokenType::Comma
13260                        || (!is_punctuation
13261                            && !matches!(
13262                                token_type,
13263                                TokenType::LParen | TokenType::RParen | TokenType::Comma
13264                            ));
13265                    self.skip();
13266                }
13267                self.expect(TokenType::RParen)?;
13268                result.push(')');
13269                result
13270            } else if self.check_identifier("ARRAY")
13271                && self
13272                    .peek_nth(1)
13273                    .is_some_and(|t| t.token_type == TokenType::LBracket)
13274            {
13275                // Handle ARRAY['value', 'value', ...] syntax (Athena/Presto)
13276                let mut result = self.advance().text.clone(); // consume ARRAY
13277                self.expect(TokenType::LBracket)?;
13278                result.push('[');
13279                let mut first = true;
13280                while !self.is_at_end() && !self.check(TokenType::RBracket) {
13281                    if !first {
13282                        if self.match_token(TokenType::Comma) {
13283                            result.push_str(", ");
13284                        } else {
13285                            break;
13286                        }
13287                    }
13288                    first = false;
13289                    // Parse array element (usually a string)
13290                    if self.check(TokenType::String) {
13291                        result.push('\'');
13292                        result.push_str(&self.advance().text);
13293                        result.push('\'');
13294                    } else if self.is_identifier_token() {
13295                        result.push_str(&self.advance().text);
13296                    } else {
13297                        break;
13298                    }
13299                }
13300                self.expect(TokenType::RBracket)?;
13301                result.push(']');
13302                result
13303            } else if self.check(TokenType::Number) {
13304                // Numeric value (e.g., bucket_count=64)
13305                self.advance().text.clone()
13306            } else {
13307                // Just an identifier or keyword (e.g., allow_page_locks=on)
13308                self.expect_identifier_or_keyword()?
13309            };
13310
13311            properties.push((key, value));
13312
13313            if !self.match_token(TokenType::Comma) {
13314                break;
13315            }
13316        }
13317
13318        self.expect(TokenType::RParen)?;
13319        Ok(properties)
13320    }
13321
13322    /// Parse column definitions and table constraints
13323    fn parse_column_definitions(&mut self) -> Result<(Vec<ColumnDef>, Vec<TableConstraint>)> {
13324        let mut columns = Vec::new();
13325        let mut constraints = Vec::new();
13326
13327        loop {
13328            if self.check(TokenType::RParen) {
13329                break;
13330            }
13331            // Check for LIKE clause (PostgreSQL)
13332            if self.check(TokenType::Like) {
13333                constraints.push(self.parse_like_clause()?);
13334            }
13335            // Check for table-level constraint
13336            // For CHECK, only treat as constraint if followed by '(' (or in ClickHouse where parens are optional).
13337            // Otherwise, 'check' is a column name (e.g., CREATE TABLE t (check INT)).
13338            else if self.check(TokenType::Constraint)
13339                || self.check(TokenType::PrimaryKey)
13340                || self.check(TokenType::ForeignKey)
13341                || self.check(TokenType::Unique)
13342                || (self.check(TokenType::Check)
13343                    && (self
13344                        .peek_nth(1)
13345                        .map_or(false, |t| t.token_type == TokenType::LParen)
13346                        || matches!(
13347                            self.config.dialect,
13348                            Some(crate::dialects::DialectType::ClickHouse)
13349                        )))
13350                || self.check(TokenType::Exclude)
13351            {
13352                constraints.push(self.parse_table_constraint()?);
13353            } else if matches!(
13354                self.config.dialect,
13355                Some(crate::dialects::DialectType::ClickHouse)
13356            ) && self.check(TokenType::Index)
13357            {
13358                // ClickHouse: INDEX name expr TYPE type_func(args) GRANULARITY n
13359                self.skip(); // consume INDEX
13360                let name = self.expect_identifier_or_keyword_with_quoted()?;
13361                // Use parse_conjunction to handle comparisons like c0 < (SELECT _table)
13362                let expression = self.parse_conjunction()?.ok_or_else(|| {
13363                    self.parse_error("Expected expression in ClickHouse INDEX definition")
13364                })?;
13365                let index_type = if self.match_token(TokenType::Type) {
13366                    // Parse function or identifier for type (e.g., bloom_filter(0.001), set(100), minmax)
13367                    // Handle keywords like 'set' that are tokenized as TokenType::Set
13368                    if let Some(func) = self.parse_function()? {
13369                        Some(Box::new(func))
13370                    } else if !self.check(TokenType::Identifier)
13371                        && !self.check(TokenType::Var)
13372                        && !self.is_at_end()
13373                    {
13374                        // Handle keywords as index type names (e.g., set, minmax)
13375                        let type_name = self.advance().text.clone();
13376                        if self.check(TokenType::LParen) {
13377                            // It's a function call like set(100)
13378                            self.skip(); // consume (
13379                            let mut args = Vec::new();
13380                            if !self.check(TokenType::RParen) {
13381                                args.push(self.parse_expression()?);
13382                                while self.match_token(TokenType::Comma) {
13383                                    args.push(self.parse_expression()?);
13384                                }
13385                            }
13386                            self.expect(TokenType::RParen)?;
13387                            Some(Box::new(Expression::Function(Box::new(Function::new(
13388                                type_name, args,
13389                            )))))
13390                        } else {
13391                            // Just an identifier
13392                            Some(Box::new(Expression::Identifier(Identifier::new(type_name))))
13393                        }
13394                    } else if let Some(id) = self.parse_id_var()? {
13395                        Some(Box::new(id))
13396                    } else {
13397                        None
13398                    }
13399                } else {
13400                    None
13401                };
13402                let granularity = if self.match_identifier("GRANULARITY") {
13403                    let gran_val = self.parse_expression()?;
13404                    Some(Box::new(gran_val))
13405                } else {
13406                    None
13407                };
13408                constraints.push(TableConstraint::Index {
13409                    name: Some(name),
13410                    columns: Vec::new(),
13411                    kind: None,
13412                    modifiers: ConstraintModifiers::default(),
13413                    use_key_keyword: false,
13414                    expression: Some(Box::new(expression)),
13415                    index_type,
13416                    granularity,
13417                });
13418            } else if !matches!(
13419                self.config.dialect,
13420                Some(crate::dialects::DialectType::ClickHouse)
13421            ) && (self.check(TokenType::Index)
13422                || self.check(TokenType::Key)
13423                || self.check_identifier("FULLTEXT")
13424                || self.check_identifier("SPATIAL"))
13425            {
13426                // INDEX/KEY constraint (MySQL). Guard KEY <type> as a normal column definition
13427                // (e.g. ClickHouse: `key UInt64`).
13428                let looks_like_key_constraint = if self.check(TokenType::Key) {
13429                    self.check_next(TokenType::LParen)
13430                        || ((self.check_next(TokenType::Identifier)
13431                            || self.check_next(TokenType::Var)
13432                            || self.check_next(TokenType::QuotedIdentifier))
13433                            && self.current + 2 < self.tokens.len()
13434                            && self.tokens[self.current + 2].token_type == TokenType::LParen)
13435                } else {
13436                    true
13437                };
13438
13439                if looks_like_key_constraint {
13440                    constraints.push(self.parse_index_table_constraint()?);
13441                } else {
13442                    columns.push(self.parse_column_def()?);
13443                }
13444            } else if self.check_identifier("PERIOD") {
13445                // TSQL: PERIOD FOR SYSTEM_TIME (start_col, end_col)
13446                if let Some(period_constraint) =
13447                    self.parse_period_for_system_time_table_constraint()?
13448                {
13449                    constraints.push(period_constraint);
13450                } else {
13451                    // Not actually PERIOD FOR SYSTEM_TIME, treat as column definition
13452                    columns.push(self.parse_column_def()?);
13453                }
13454            } else if self.check_identifier("INITIALLY") {
13455                // PostgreSQL: INITIALLY DEFERRED / INITIALLY IMMEDIATE as table-level setting
13456                self.skip(); // consume INITIALLY
13457                if self.match_identifier("DEFERRED") {
13458                    constraints.push(TableConstraint::InitiallyDeferred { deferred: true });
13459                } else if self.match_identifier("IMMEDIATE") {
13460                    constraints.push(TableConstraint::InitiallyDeferred { deferred: false });
13461                } else {
13462                    return Err(self.parse_error("Expected DEFERRED or IMMEDIATE after INITIALLY"));
13463                }
13464            } else if matches!(
13465                self.config.dialect,
13466                Some(crate::dialects::DialectType::ClickHouse)
13467            ) && self.check_identifier("PROJECTION")
13468            {
13469                // ClickHouse: PROJECTION name (SELECT ...) or PROJECTION name INDEX expr TYPE type_name
13470                self.skip(); // consume PROJECTION
13471                let name = self.expect_identifier_or_keyword_with_quoted()?;
13472                if self.match_token(TokenType::LParen) {
13473                    let expression = self.parse_statement()?;
13474                    self.expect(TokenType::RParen)?;
13475                    // ClickHouse: PROJECTION name (SELECT ...) WITH SETTINGS (key=value, ...)
13476                    if self.check(TokenType::With)
13477                        && self.current + 1 < self.tokens.len()
13478                        && self.tokens[self.current + 1].token_type == TokenType::Settings
13479                    {
13480                        self.skip(); // consume WITH
13481                        self.skip(); // consume SETTINGS
13482                        if self.match_token(TokenType::LParen) {
13483                            // Consume key=value pairs
13484                            loop {
13485                                if self.check(TokenType::RParen) {
13486                                    break;
13487                                }
13488                                if self.is_identifier_token()
13489                                    || self.is_safe_keyword_as_identifier()
13490                                {
13491                                    self.skip(); // key
13492                                }
13493                                if self.match_token(TokenType::Eq) {
13494                                    let _ = self.parse_primary()?; // value
13495                                }
13496                                if !self.match_token(TokenType::Comma) {
13497                                    break;
13498                                }
13499                            }
13500                            self.expect(TokenType::RParen)?;
13501                        }
13502                    }
13503                    constraints.push(TableConstraint::Projection { name, expression });
13504                } else if self.match_token(TokenType::Index) {
13505                    // PROJECTION name INDEX expr TYPE type_name
13506                    let expr = self.parse_bitwise()?.ok_or_else(|| {
13507                        self.parse_error(
13508                            "Expected expression in ClickHouse PROJECTION INDEX definition",
13509                        )
13510                    })?;
13511                    let type_str = if self.match_token(TokenType::Type) {
13512                        if !self.is_at_end()
13513                            && !self.check(TokenType::Comma)
13514                            && !self.check(TokenType::RParen)
13515                        {
13516                            self.advance().text.clone()
13517                        } else {
13518                            String::new()
13519                        }
13520                    } else {
13521                        String::new()
13522                    };
13523                    let raw_sql = if type_str.is_empty() {
13524                        format!("INDEX {} ", expr)
13525                    } else {
13526                        format!("INDEX {} TYPE {}", expr, type_str)
13527                    };
13528                    constraints.push(TableConstraint::Projection {
13529                        name,
13530                        expression: Expression::Raw(Raw { sql: raw_sql }),
13531                    });
13532                } else {
13533                    constraints.push(TableConstraint::Projection {
13534                        name,
13535                        expression: Expression::Null(Null),
13536                    });
13537                }
13538            } else {
13539                // Parse column definition
13540                columns.push(self.parse_column_def()?);
13541            }
13542
13543            if !self.match_token(TokenType::Comma) {
13544                break;
13545            }
13546            // ClickHouse: allow trailing comma before closing paren
13547            if matches!(
13548                self.config.dialect,
13549                Some(crate::dialects::DialectType::ClickHouse)
13550            ) && self.check(TokenType::RParen)
13551            {
13552                break;
13553            }
13554        }
13555
13556        Ok((columns, constraints))
13557    }
13558
13559    /// Parse LIKE clause in CREATE TABLE: LIKE source_table [INCLUDING|EXCLUDING options]
13560    fn parse_like_clause(&mut self) -> Result<TableConstraint> {
13561        self.expect(TokenType::Like)?;
13562        let source = self.parse_table_ref()?;
13563        let mut options = Vec::new();
13564
13565        // Parse optional INCLUDING/EXCLUDING modifiers
13566        loop {
13567            if self.match_identifier("INCLUDING") {
13568                let prop = self.expect_identifier_or_keyword()?.to_ascii_uppercase();
13569                options.push((LikeOptionAction::Including, prop));
13570            } else if self.match_identifier("EXCLUDING") {
13571                let prop = self.expect_identifier_or_keyword()?.to_ascii_uppercase();
13572                options.push((LikeOptionAction::Excluding, prop));
13573            } else {
13574                break;
13575            }
13576        }
13577
13578        Ok(TableConstraint::Like { source, options })
13579    }
13580
13581    /// Parse a single column definition
13582    fn parse_column_def(&mut self) -> Result<ColumnDef> {
13583        // Column names can be keywords like 'end', 'truncate', 'view', etc.
13584        // ClickHouse allows any keyword as column name (from, select, etc.)
13585        let mut name = if matches!(
13586            self.config.dialect,
13587            Some(crate::dialects::DialectType::ClickHouse)
13588        ) {
13589            self.expect_identifier_or_keyword_with_quoted()?
13590        } else {
13591            self.expect_identifier_or_safe_keyword_with_quoted()?
13592        };
13593        // ClickHouse: Nested column names like n.b for Nested() columns
13594        if matches!(
13595            self.config.dialect,
13596            Some(crate::dialects::DialectType::ClickHouse)
13597        ) {
13598            while self.match_token(TokenType::Dot) {
13599                let sub = self.expect_identifier_or_safe_keyword_with_quoted()?;
13600                name = Identifier {
13601                    name: format!("{}.{}", name.name, sub.name),
13602                    quoted: name.quoted,
13603                    trailing_comments: sub.trailing_comments,
13604                    span: None,
13605                };
13606            }
13607        }
13608
13609        // TSQL computed columns have no data type: column_name AS (expression) [PERSISTED]
13610        // Check if AS follows immediately (no data type)
13611        if self.check(TokenType::As) {
13612            let mut col_def = ColumnDef::new(
13613                name.name.clone(),
13614                DataType::Custom {
13615                    name: String::new(),
13616                },
13617            );
13618            col_def.name = name;
13619            // Consume AS and parse computed column expression
13620            self.skip(); // consume AS
13621            if self.check(TokenType::LParen) {
13622                self.parse_as_computed_column(&mut col_def)?;
13623            }
13624            return Ok(col_def);
13625        }
13626
13627        // SQLite allows column definitions without types: CREATE TABLE t (x, y)
13628        // ClickHouse allows typeless columns with DEFAULT/MATERIALIZED/ALIAS/EPHEMERAL
13629        // Check if the next token indicates no type (comma, rparen, or constraint keyword)
13630        let no_type = self.check(TokenType::Comma)
13631            || self.check(TokenType::RParen)
13632            || (matches!(
13633                self.config.dialect,
13634                Some(crate::dialects::DialectType::ClickHouse)
13635            ) && (self.check(TokenType::Default)
13636                || self.check(TokenType::Materialized)
13637                || self.check_identifier("ALIAS")
13638                || self.check_identifier("EPHEMERAL")));
13639        let data_type = if no_type {
13640            // No type specified - use empty custom type
13641            DataType::Custom {
13642                name: String::new(),
13643            }
13644        } else {
13645            self.parse_data_type()?
13646        };
13647
13648        let mut col_def = ColumnDef::new(name.name.clone(), data_type);
13649        col_def.name = name;
13650        col_def.no_type = no_type;
13651
13652        // Parse MySQL type modifiers (UNSIGNED, ZEROFILL)
13653        // These come after the data type but before other constraints
13654        while self.match_identifier("UNSIGNED")
13655            || self.match_identifier("ZEROFILL")
13656            || self.match_identifier("SIGNED")
13657        {
13658            let modifier = self.previous().text.to_ascii_uppercase();
13659            if modifier == "UNSIGNED" {
13660                col_def.unsigned = true;
13661            } else if modifier == "ZEROFILL" {
13662                col_def.zerofill = true;
13663            }
13664            // SIGNED is the default, no action needed
13665        }
13666
13667        // BigQuery: OPTIONS (key=value, ...) on column - comes right after type
13668        if self.match_identifier("OPTIONS") {
13669            col_def.options = self.parse_options_list()?;
13670        }
13671
13672        // Parse column constraints
13673        loop {
13674            if self.match_keywords(&[TokenType::Not, TokenType::Null]) {
13675                col_def.nullable = Some(false);
13676                col_def.constraint_order.push(ConstraintType::NotNull);
13677            } else if self.match_token(TokenType::Null) {
13678                col_def.nullable = Some(true);
13679                col_def.constraint_order.push(ConstraintType::Null);
13680            } else if self.match_keywords(&[TokenType::PrimaryKey, TokenType::Key]) {
13681                // Handle PRIMARY KEY [ASC|DESC]
13682                col_def.primary_key = true;
13683                // Capture ASC/DESC after PRIMARY KEY
13684                if self.match_token(TokenType::Asc) {
13685                    col_def.primary_key_order = Some(SortOrder::Asc);
13686                } else if self.match_token(TokenType::Desc) {
13687                    col_def.primary_key_order = Some(SortOrder::Desc);
13688                }
13689                col_def.constraint_order.push(ConstraintType::PrimaryKey);
13690            } else if self.match_token(TokenType::Constraint) {
13691                // Inline CONSTRAINT name ... (e.g., CONSTRAINT fk_name REFERENCES ...)
13692                let constraint_name = self.expect_identifier()?;
13693                // After constraint name, expect REFERENCES, PRIMARY KEY, UNIQUE, CHECK, NOT NULL, NULL, etc.
13694                if self.match_token(TokenType::References) {
13695                    let mut fk_ref = self.parse_foreign_key_ref()?;
13696                    fk_ref.constraint_name = Some(constraint_name);
13697                    col_def
13698                        .constraints
13699                        .push(ColumnConstraint::References(fk_ref));
13700                    col_def.constraint_order.push(ConstraintType::References);
13701                } else if self.match_keywords(&[TokenType::PrimaryKey, TokenType::Key]) {
13702                    col_def.primary_key = true;
13703                    col_def.primary_key_constraint_name = Some(constraint_name);
13704                    col_def.constraint_order.push(ConstraintType::PrimaryKey);
13705                } else if self.match_token(TokenType::Unique) {
13706                    col_def.unique = true;
13707                    col_def.unique_constraint_name = Some(constraint_name);
13708                    // Check for NULLS NOT DISTINCT (PostgreSQL 15+ feature)
13709                    if self.match_text_seq(&["NULLS", "NOT", "DISTINCT"]) {
13710                        col_def.unique_nulls_not_distinct = true;
13711                    }
13712                    col_def.constraint_order.push(ConstraintType::Unique);
13713                } else if self.match_keywords(&[TokenType::Not, TokenType::Null]) {
13714                    col_def.nullable = Some(false);
13715                    col_def.not_null_constraint_name = Some(constraint_name);
13716                    col_def.constraint_order.push(ConstraintType::NotNull);
13717                } else if self.match_token(TokenType::Check) {
13718                    col_def.check_constraint_name = Some(constraint_name);
13719                    // Parse CHECK constraint expression
13720                    if self.match_token(TokenType::LParen) {
13721                        let check_expr = self.parse_expression()?;
13722                        self.expect(TokenType::RParen)?;
13723                        col_def
13724                            .constraints
13725                            .push(ColumnConstraint::Check(check_expr));
13726                    } else if matches!(
13727                        self.config.dialect,
13728                        Some(crate::dialects::DialectType::ClickHouse)
13729                    ) {
13730                        // ClickHouse: CHECK expr without parens
13731                        let check_expr = self.parse_or()?;
13732                        col_def
13733                            .constraints
13734                            .push(ColumnConstraint::Check(check_expr));
13735                    }
13736                    col_def.constraint_order.push(ConstraintType::Check);
13737                }
13738            } else if self.match_token(TokenType::Unique) {
13739                col_def.unique = true;
13740                // Check for NULLS NOT DISTINCT (PostgreSQL 15+ feature)
13741                if self.match_text_seq(&["NULLS", "NOT", "DISTINCT"]) {
13742                    col_def.unique_nulls_not_distinct = true;
13743                }
13744                col_def.constraint_order.push(ConstraintType::Unique);
13745            } else if self.match_token(TokenType::Check) {
13746                // Standalone CHECK (expr) constraint (without CONSTRAINT name)
13747                if self.match_token(TokenType::LParen) {
13748                    let check_expr = self.parse_expression()?;
13749                    self.expect(TokenType::RParen)?;
13750                    col_def
13751                        .constraints
13752                        .push(ColumnConstraint::Check(check_expr));
13753                    col_def.constraint_order.push(ConstraintType::Check);
13754                } else if matches!(
13755                    self.config.dialect,
13756                    Some(crate::dialects::DialectType::ClickHouse)
13757                ) {
13758                    // ClickHouse: CHECK expr without parens
13759                    let check_expr = self.parse_or()?;
13760                    col_def
13761                        .constraints
13762                        .push(ColumnConstraint::Check(check_expr));
13763                    col_def.constraint_order.push(ConstraintType::Check);
13764                }
13765            } else if self.match_token(TokenType::AutoIncrement) || self.match_keyword("IDENTITY") {
13766                col_def.auto_increment = true;
13767                col_def.constraint_order.push(ConstraintType::AutoIncrement);
13768                // Handle IDENTITY/AUTOINCREMENT options: START n INCREMENT m [ORDER|NOORDER] or (start, increment)
13769                if self.match_keyword("START") {
13770                    col_def.auto_increment_start = Some(Box::new(self.parse_primary()?));
13771                    if self.match_keyword("INCREMENT") {
13772                        col_def.auto_increment_increment = Some(Box::new(self.parse_primary()?));
13773                    }
13774                    // Snowflake: ORDER or NOORDER option
13775                    if self.match_token(TokenType::Order) {
13776                        col_def.auto_increment_order = Some(true);
13777                    } else if self.match_identifier("NOORDER") {
13778                        col_def.auto_increment_order = Some(false);
13779                    }
13780                } else if self.match_token(TokenType::LParen) {
13781                    // IDENTITY(start, increment) or AUTOINCREMENT(start, increment)
13782                    col_def.auto_increment_start = Some(Box::new(self.parse_primary()?));
13783                    if self.match_token(TokenType::Comma) {
13784                        col_def.auto_increment_increment = Some(Box::new(self.parse_primary()?));
13785                    }
13786                    self.expect(TokenType::RParen)?;
13787                }
13788            } else if self.match_token(TokenType::Default) {
13789                // ClickHouse: DEFAULT expressions can be complex (today(), a + 1, cond ? x : y, etc.)
13790                col_def.default = if matches!(
13791                    self.config.dialect,
13792                    Some(crate::dialects::DialectType::ClickHouse)
13793                ) {
13794                    Some(self.parse_expression()?)
13795                } else {
13796                    Some(self.parse_unary()?)
13797                };
13798                col_def.constraint_order.push(ConstraintType::Default);
13799            } else if self.match_keywords(&[TokenType::ForeignKey, TokenType::Key]) {
13800                // Snowflake/SQL Server: FOREIGN KEY REFERENCES table(columns)
13801                // The FOREIGN KEY keywords are followed by REFERENCES
13802                self.expect(TokenType::References)?;
13803                let mut fk_ref = self.parse_foreign_key_ref()?;
13804                fk_ref.has_foreign_key_keywords = true;
13805                col_def
13806                    .constraints
13807                    .push(ColumnConstraint::References(fk_ref));
13808                col_def.constraint_order.push(ConstraintType::References);
13809            } else if self.match_token(TokenType::References) {
13810                let fk_ref = self.parse_foreign_key_ref()?;
13811                col_def
13812                    .constraints
13813                    .push(ColumnConstraint::References(fk_ref));
13814                col_def.constraint_order.push(ConstraintType::References);
13815            } else if self.match_token(TokenType::Generated) {
13816                // GENERATED [BY DEFAULT [ON NULL] | ALWAYS] AS ...
13817                // Could be: AS IDENTITY, AS (expr) STORED|VIRTUAL, AS ROW START|END
13818                self.parse_generated_column_constraint(&mut col_def)?;
13819            } else if self.match_token(TokenType::Collate) {
13820                // COLLATE collation_name (may be quoted like "de_DE")
13821                // Also handle dotted names like pg_catalog."default"
13822                let mut collation = self.expect_identifier_or_keyword_with_quoted()?;
13823                // Check for dotted collation names: pg_catalog."default"
13824                while self.match_token(TokenType::Dot) {
13825                    let next = self.expect_identifier_or_keyword_with_quoted()?;
13826                    let sep = if next.quoted {
13827                        format!("{}.\"{}\"", collation.name, next.name)
13828                    } else {
13829                        format!("{}.{}", collation.name, next.name)
13830                    };
13831                    collation = Identifier {
13832                        name: sep,
13833                        quoted: false,
13834                        trailing_comments: Vec::new(),
13835                        span: None,
13836                    };
13837                }
13838                col_def
13839                    .constraints
13840                    .push(ColumnConstraint::Collate(collation));
13841                col_def.constraint_order.push(ConstraintType::Collate);
13842            } else if self.match_token(TokenType::Comment) {
13843                // COMMENT 'comment text'
13844                let comment_text = self.expect_string()?;
13845                col_def
13846                    .constraints
13847                    .push(ColumnConstraint::Comment(comment_text));
13848                col_def.constraint_order.push(ConstraintType::Comment);
13849            } else if self.match_keywords(&[TokenType::On, TokenType::Update]) {
13850                // MySQL: ON UPDATE expression (e.g., ON UPDATE CURRENT_TIMESTAMP)
13851                let expr = self.parse_unary()?;
13852                col_def.on_update = Some(expr);
13853                col_def.constraint_order.push(ConstraintType::OnUpdate);
13854            } else if self.match_identifier("ENCODE") {
13855                // Redshift: ENCODE encoding_type (e.g., ZSTD, DELTA, LZO, etc.)
13856                let encoding = self.expect_identifier_or_keyword()?;
13857                col_def.encoding = Some(encoding);
13858                col_def.constraint_order.push(ConstraintType::Encode);
13859            } else if !matches!(
13860                self.config.dialect,
13861                Some(crate::dialects::DialectType::ClickHouse)
13862            ) && self.match_token(TokenType::Format)
13863            {
13864                // Teradata: FORMAT 'pattern' (not ClickHouse — FORMAT there is statement-level)
13865                let format_str = self.expect_string()?;
13866                col_def.format = Some(format_str);
13867            } else if self.match_identifier("TITLE") {
13868                // Teradata: TITLE 'title'
13869                let title_str = self.expect_string()?;
13870                col_def.title = Some(title_str);
13871            } else if self.match_identifier("INLINE") {
13872                // Teradata: INLINE LENGTH n
13873                self.match_identifier("LENGTH");
13874                let length = self.expect_number()?;
13875                col_def.inline_length = Some(length as u64);
13876            } else if self.match_identifier("COMPRESS") {
13877                // Teradata: COMPRESS or COMPRESS (values) or COMPRESS 'value'
13878                if self.match_token(TokenType::LParen) {
13879                    let values = self.parse_expression_list()?;
13880                    self.expect(TokenType::RParen)?;
13881                    col_def.compress = Some(values);
13882                } else if self.check(TokenType::String) {
13883                    // COMPRESS 'value'
13884                    let value = self.parse_primary()?;
13885                    col_def.compress = Some(vec![value]);
13886                } else {
13887                    // COMPRESS without values
13888                    col_def.compress = Some(Vec::new());
13889                }
13890            } else if self.match_identifier("CHARACTER") {
13891                // Teradata: CHARACTER SET name
13892                self.match_token(TokenType::Set);
13893                let charset = self.expect_identifier_or_keyword()?;
13894                col_def.character_set = Some(charset);
13895            } else if self.match_identifier("UPPERCASE") {
13896                // Teradata: UPPERCASE
13897                col_def.uppercase = true;
13898            } else if self.match_identifier("CASESPECIFIC") {
13899                // Teradata: CASESPECIFIC
13900                col_def.casespecific = Some(true);
13901            } else if self.match_text_seq(&["NOT", "FOR", "REPLICATION"]) {
13902                // TSQL: NOT FOR REPLICATION - skip this modifier (not preserved in output for non-TSQL)
13903                col_def.not_for_replication = true;
13904            } else if self.match_token(TokenType::Not) && self.match_identifier("CASESPECIFIC") {
13905                // Teradata: NOT CASESPECIFIC
13906                col_def.casespecific = Some(false);
13907            } else if self.match_keyword("TAG")
13908                || (self.match_token(TokenType::With) && self.match_keyword("TAG"))
13909            {
13910                // Snowflake: TAG (key='value', ...) or WITH TAG (key='value', ...)
13911                let tags = self.parse_tags()?;
13912                col_def.constraints.push(ColumnConstraint::Tags(tags));
13913                col_def.constraint_order.push(ConstraintType::Tags);
13914            } else if self.match_token(TokenType::As) {
13915                // Computed column: AS (expression) [STORED|VIRTUAL|PERSISTED] [NOT NULL]
13916                // TSQL: AS (expression) [PERSISTED] [NOT NULL]
13917                // MySQL shorthand: AS (expression) [STORED|VIRTUAL]
13918                // Also: Snowflake External Table virtual column expression
13919                if self.check(TokenType::LParen) {
13920                    self.parse_as_computed_column(&mut col_def)?;
13921                }
13922            } else if self.match_identifier("CODEC") {
13923                // ClickHouse: CODEC(LZ4HC(9), ZSTD, DELTA)
13924                self.expect(TokenType::LParen)?;
13925                let start = self.current;
13926                let mut depth = 1;
13927                while !self.is_at_end() && depth > 0 {
13928                    if self.check(TokenType::LParen) {
13929                        depth += 1;
13930                    }
13931                    if self.check(TokenType::RParen) {
13932                        depth -= 1;
13933                        if depth == 0 {
13934                            break;
13935                        }
13936                    }
13937                    self.skip();
13938                }
13939                let codec_text = self.tokens_to_sql(start, self.current);
13940                self.expect(TokenType::RParen)?;
13941                col_def.codec = Some(codec_text);
13942            } else if self.match_identifier("STATISTICS") {
13943                // ClickHouse: STATISTICS(tdigest, minmax, uniq, ...)
13944                self.expect(TokenType::LParen)?;
13945                let mut depth = 1;
13946                while !self.is_at_end() && depth > 0 {
13947                    if self.check(TokenType::LParen) {
13948                        depth += 1;
13949                    }
13950                    if self.check(TokenType::RParen) {
13951                        depth -= 1;
13952                        if depth == 0 {
13953                            break;
13954                        }
13955                    }
13956                    self.skip();
13957                }
13958                self.expect(TokenType::RParen)?;
13959                // Statistics info is stored but we don't need it for transpilation
13960            } else if self.match_identifier("EPHEMERAL") {
13961                // ClickHouse: EPHEMERAL [expr] [type]
13962                // EPHEMERAL can optionally be followed by an expression, then optionally a data type
13963                if !self.check(TokenType::Comma)
13964                    && !self.check(TokenType::RParen)
13965                    && !self.is_at_end()
13966                    && !self.check_identifier("CODEC")
13967                    && !self.check_identifier("TTL")
13968                    && !self.check(TokenType::Comment)
13969                {
13970                    let expr = self.parse_bitwise()?.unwrap_or(Expression::Null(Null));
13971                    col_def.ephemeral = Some(Some(Box::new(expr)));
13972                    // ClickHouse: type can follow EPHEMERAL expression (e.g., b EPHEMERAL 'a' String)
13973                    if col_def.no_type
13974                        && !self.check(TokenType::Comma)
13975                        && !self.check(TokenType::RParen)
13976                        && !self.is_at_end()
13977                        && !self.check_identifier("CODEC")
13978                        && !self.check_identifier("TTL")
13979                        && !self.check(TokenType::Comment)
13980                    {
13981                        col_def.data_type = self.parse_data_type()?;
13982                        col_def.no_type = false;
13983                    }
13984                } else {
13985                    col_def.ephemeral = Some(None);
13986                }
13987            } else if self.check(TokenType::Materialized) && !self.check_next(TokenType::View) {
13988                // ClickHouse: MATERIALIZED expr (but not MATERIALIZED VIEW)
13989                self.skip(); // consume MATERIALIZED
13990                let expr = self.parse_or()?;
13991                col_def.materialized_expr = Some(Box::new(expr));
13992            } else if self.match_identifier("ALIAS") {
13993                // ClickHouse: ALIAS expr
13994                let expr = self.parse_or()?;
13995                col_def.alias_expr = Some(Box::new(expr));
13996            } else if matches!(
13997                self.config.dialect,
13998                Some(crate::dialects::DialectType::ClickHouse)
13999            ) && self.check_identifier("EXPRESSION")
14000            {
14001                // ClickHouse dictionary column: EXPRESSION expr
14002                self.skip(); // consume EXPRESSION
14003                let expr = self.parse_or()?;
14004                col_def.materialized_expr = Some(Box::new(expr));
14005            } else if matches!(
14006                self.config.dialect,
14007                Some(crate::dialects::DialectType::ClickHouse)
14008            ) && (self.match_identifier("HIERARCHICAL")
14009                || self.match_identifier("IS_OBJECT_ID")
14010                || self.match_identifier("INJECTIVE")
14011                || self.match_identifier("BIDIRECTIONAL"))
14012            {
14013                // ClickHouse dictionary column attributes: HIERARCHICAL, IS_OBJECT_ID, INJECTIVE, BIDIRECTIONAL
14014                // These are flag-like attributes with no value, just skip them
14015            } else if self.match_identifier("TTL") {
14016                // ClickHouse: TTL expr
14017                let expr = self.parse_expression()?;
14018                col_def.ttl_expr = Some(Box::new(expr));
14019            } else if matches!(
14020                self.config.dialect,
14021                Some(crate::dialects::DialectType::ClickHouse)
14022            ) && self.check(TokenType::Settings)
14023                && self.check_next(TokenType::LParen)
14024            {
14025                // ClickHouse: SETTINGS (key = value, ...) on column definition
14026                // Only match parenthesized form; non-parenthesized SETTINGS is statement-level
14027                self.skip(); // consume SETTINGS
14028                self.expect(TokenType::LParen)?;
14029                let mut depth = 1i32;
14030                while !self.is_at_end() && depth > 0 {
14031                    if self.check(TokenType::LParen) {
14032                        depth += 1;
14033                    }
14034                    if self.check(TokenType::RParen) {
14035                        depth -= 1;
14036                        if depth == 0 {
14037                            break;
14038                        }
14039                    }
14040                    self.skip();
14041                }
14042                self.expect(TokenType::RParen)?;
14043            } else {
14044                // Skip unknown column modifiers (DEFERRABLE, CHARACTER SET, etc.)
14045                // to allow parsing to continue
14046                if self.skip_column_modifier() {
14047                    continue;
14048                }
14049                break;
14050            }
14051        }
14052
14053        Ok(col_def)
14054    }
14055
14056    /// Skip optional column modifiers that we don't need to preserve
14057    fn skip_column_modifier(&mut self) -> bool {
14058        // NOT DEFERRABLE, NOT CASESPECIFIC - handle NOT followed by specific keywords
14059        // (NOT NULL is handled earlier in the constraint loop)
14060        if self.check(TokenType::Not) {
14061            // Check what follows NOT
14062            if self.check_next_identifier("DEFERRABLE")
14063                || self.check_next_identifier("CASESPECIFIC")
14064            {
14065                self.skip(); // consume NOT
14066                self.skip(); // consume DEFERRABLE/CASESPECIFIC
14067                return true;
14068            }
14069        }
14070        // DEFERRABLE / NOT DEFERRABLE / INITIALLY DEFERRED / INITIALLY IMMEDIATE
14071        if self.match_identifier("DEFERRABLE")
14072            || self.match_identifier("DEFERRED")
14073            || self.match_identifier("IMMEDIATE")
14074        {
14075            return true;
14076        }
14077        // CHARACTER SET name
14078        if self.match_identifier("CHARACTER") {
14079            self.match_token(TokenType::Set);
14080            // Consume charset name (can be multiple parts like LATIN, utf8_bin, etc.)
14081            let _ = self.match_token(TokenType::Var) || self.match_token(TokenType::Identifier);
14082            return true;
14083        }
14084        // UPPERCASE, CASESPECIFIC
14085        if self.match_identifier("UPPERCASE") || self.match_identifier("CASESPECIFIC") {
14086            return true;
14087        }
14088        // Note: COMPRESS, FORMAT, TITLE, and INLINE LENGTH are now properly parsed and stored in ColumnDef
14089        false
14090    }
14091
14092    /// Parse Teradata-specific table options after CREATE TABLE AS
14093    /// Returns (with_data, with_statistics, teradata_indexes)
14094    fn parse_teradata_table_options(&mut self) -> (Option<bool>, Option<bool>, Vec<TeradataIndex>) {
14095        let mut with_data = None;
14096        let mut with_statistics = None;
14097        let mut teradata_indexes = Vec::new();
14098
14099        loop {
14100            // WITH DATA [AND STATISTICS] / WITH NO DATA [AND NO STATISTICS]
14101            if self.match_token(TokenType::With) {
14102                let no = self.match_token(TokenType::No); // optional NO
14103                self.match_identifier("DATA");
14104                with_data = Some(!no); // WITH DATA = true, WITH NO DATA = false
14105                                       // Optional AND [NO] STATISTICS
14106                if self.match_token(TokenType::And) {
14107                    let no_stats = self.match_token(TokenType::No); // optional NO
14108                    self.match_identifier("STATISTICS");
14109                    with_statistics = Some(!no_stats); // AND STATISTICS = true, AND NO STATISTICS = false
14110                }
14111                continue;
14112            }
14113            // NO PRIMARY INDEX
14114            if self.match_token(TokenType::No) {
14115                self.match_token(TokenType::PrimaryKey);
14116                self.match_token(TokenType::Index);
14117                teradata_indexes.push(TeradataIndex {
14118                    kind: TeradataIndexKind::NoPrimary,
14119                    name: None,
14120                    columns: Vec::new(),
14121                });
14122                // Consume optional comma separator between index specs
14123                self.match_token(TokenType::Comma);
14124                continue;
14125            }
14126            // PRIMARY AMP INDEX / PRIMARY INDEX
14127            if self.match_token(TokenType::PrimaryKey) {
14128                let is_amp = self.match_identifier("AMP");
14129                self.match_token(TokenType::Index);
14130                // Optional index name
14131                let name = if self.is_identifier_token() && !self.check(TokenType::LParen) {
14132                    Some(self.advance().text)
14133                } else {
14134                    None
14135                };
14136                // Optional column list
14137                let columns = if self.match_token(TokenType::LParen) {
14138                    let cols = self.parse_identifier_list_raw();
14139                    self.match_token(TokenType::RParen);
14140                    cols
14141                } else {
14142                    Vec::new()
14143                };
14144                teradata_indexes.push(TeradataIndex {
14145                    kind: if is_amp {
14146                        TeradataIndexKind::PrimaryAmp
14147                    } else {
14148                        TeradataIndexKind::Primary
14149                    },
14150                    name,
14151                    columns,
14152                });
14153                // Consume optional comma separator between index specs
14154                self.match_token(TokenType::Comma);
14155                continue;
14156            }
14157            // UNIQUE [PRIMARY] INDEX
14158            if self.match_token(TokenType::Unique) {
14159                let is_primary = self.match_token(TokenType::PrimaryKey);
14160                self.match_token(TokenType::Index);
14161                // Optional index name
14162                let name = if self.is_identifier_token() {
14163                    Some(self.advance().text)
14164                } else {
14165                    None
14166                };
14167                // Optional column list
14168                let columns = if self.match_token(TokenType::LParen) {
14169                    let cols = self.parse_identifier_list_raw();
14170                    self.match_token(TokenType::RParen);
14171                    cols
14172                } else {
14173                    Vec::new()
14174                };
14175                teradata_indexes.push(TeradataIndex {
14176                    kind: if is_primary {
14177                        TeradataIndexKind::UniquePrimary
14178                    } else {
14179                        TeradataIndexKind::Unique
14180                    },
14181                    name,
14182                    columns,
14183                });
14184                // Consume optional comma separator between index specs
14185                self.match_token(TokenType::Comma);
14186                continue;
14187            }
14188            // Plain INDEX (non-primary, non-unique)
14189            if self.match_token(TokenType::Index) {
14190                // Optional index name
14191                let name = if self.is_identifier_token() && !self.check(TokenType::LParen) {
14192                    Some(self.advance().text)
14193                } else {
14194                    None
14195                };
14196                // Optional column list
14197                let columns = if self.match_token(TokenType::LParen) {
14198                    let cols = self.parse_identifier_list_raw();
14199                    self.match_token(TokenType::RParen);
14200                    cols
14201                } else {
14202                    Vec::new()
14203                };
14204                teradata_indexes.push(TeradataIndex {
14205                    kind: TeradataIndexKind::Secondary,
14206                    name,
14207                    columns,
14208                });
14209                // Consume optional comma separator between index specs
14210                self.match_token(TokenType::Comma);
14211                continue;
14212            }
14213            break;
14214        }
14215
14216        (with_data, with_statistics, teradata_indexes)
14217    }
14218
14219    /// Parse Teradata table options after name before column list (comma-separated)
14220    fn parse_teradata_post_name_options(&mut self) -> Vec<String> {
14221        // Options begin with a comma after the table name.
14222        if !self.match_token(TokenType::Comma) {
14223            return Vec::new();
14224        }
14225
14226        let mut options = Vec::new();
14227        let mut current_tokens: Vec<(String, TokenType)> = Vec::new();
14228        let mut paren_depth = 0;
14229        let mut in_value = false;
14230
14231        while !self.is_at_end() {
14232            if self.check(TokenType::LParen) && paren_depth == 0 {
14233                if !in_value {
14234                    // Column list begins
14235                    break;
14236                }
14237                let mut is_terminal = false;
14238                if let Some((last_text, last_type)) = current_tokens.last() {
14239                    let last_upper = last_text.to_ascii_uppercase();
14240                    is_terminal = matches!(last_type, TokenType::Number | TokenType::String)
14241                        || matches!(
14242                            last_upper.as_str(),
14243                            "ON" | "OFF"
14244                                | "DEFAULT"
14245                                | "NEVER"
14246                                | "ALWAYS"
14247                                | "MINIMUM"
14248                                | "MAXIMUM"
14249                                | "BYTES"
14250                                | "KBYTES"
14251                                | "KILOBYTES"
14252                                | "PERCENT"
14253                        );
14254                }
14255                if is_terminal {
14256                    break;
14257                }
14258            }
14259
14260            let token = self.advance();
14261
14262            match token.token_type {
14263                TokenType::LParen => {
14264                    paren_depth += 1;
14265                }
14266                TokenType::RParen => {
14267                    if paren_depth > 0 {
14268                        paren_depth -= 1;
14269                        if paren_depth == 0 && in_value {
14270                            in_value = false;
14271                        }
14272                    }
14273                }
14274                TokenType::Eq => {
14275                    if paren_depth == 0 {
14276                        in_value = true;
14277                    }
14278                }
14279                TokenType::Comma => {
14280                    if paren_depth == 0 {
14281                        let option = self.join_teradata_option_tokens(current_tokens);
14282                        if !option.is_empty() {
14283                            options.push(option);
14284                        }
14285                        current_tokens = Vec::new();
14286                        in_value = false;
14287                        continue;
14288                    }
14289                }
14290                _ => {}
14291            }
14292
14293            let text = if token.token_type == TokenType::QuotedIdentifier {
14294                let quote_char = if self.config.dialect == Some(crate::dialects::DialectType::MySQL)
14295                    || self.config.dialect == Some(crate::dialects::DialectType::SingleStore)
14296                    || self.config.dialect == Some(crate::dialects::DialectType::Doris)
14297                    || self.config.dialect == Some(crate::dialects::DialectType::StarRocks)
14298                {
14299                    '`'
14300                } else {
14301                    '"'
14302                };
14303                format!("{}{}{}", quote_char, token.text, quote_char)
14304            } else if token.token_type == TokenType::String {
14305                format!("'{}'", token.text)
14306            } else {
14307                token.text.clone()
14308            };
14309
14310            let mut join_type = token.token_type;
14311            if join_type == TokenType::Percent && token.text.eq_ignore_ascii_case("PERCENT") {
14312                // Treat PERCENT as an identifier to preserve spacing (e.g., "1 PERCENT")
14313                join_type = TokenType::Identifier;
14314            }
14315            current_tokens.push((text, join_type));
14316        }
14317
14318        if !current_tokens.is_empty() {
14319            let option = self.join_teradata_option_tokens(current_tokens);
14320            if !option.is_empty() {
14321                options.push(option);
14322            }
14323        }
14324
14325        options
14326    }
14327
14328    /// Parse identifier list for Teradata indexes, returning raw strings
14329    fn parse_identifier_list_raw(&mut self) -> Vec<String> {
14330        let mut identifiers = Vec::new();
14331        loop {
14332            if self.is_identifier_token() || self.is_identifier_or_keyword_token() {
14333                identifiers.push(self.advance().text);
14334            }
14335            if !self.match_token(TokenType::Comma) {
14336                break;
14337            }
14338        }
14339        identifiers
14340    }
14341
14342    /// Parse GENERATED column constraint after GENERATED token has been consumed.
14343    /// Handles three forms:
14344    /// 1. GENERATED [BY DEFAULT | ALWAYS] AS IDENTITY [...] -> GeneratedAsIdentity
14345    /// 2. GENERATED ALWAYS AS (expr) [STORED|VIRTUAL] -> ComputedColumn
14346    /// 3. GENERATED ALWAYS AS ROW START|END [HIDDEN] -> GeneratedAsRow
14347    fn parse_generated_column_constraint(&mut self, col_def: &mut ColumnDef) -> Result<()> {
14348        let always;
14349        let mut on_null = false;
14350
14351        // BY DEFAULT [ON NULL] | ALWAYS
14352        if self.match_token(TokenType::By) {
14353            self.expect(TokenType::Default)?;
14354            on_null = self.match_keywords(&[TokenType::On, TokenType::Null]);
14355            always = false;
14356        } else {
14357            self.expect(TokenType::Always)?;
14358            always = true;
14359        }
14360
14361        // Expect AS
14362        self.expect(TokenType::As)?;
14363
14364        // Check what follows AS
14365        if self.check(TokenType::Row) {
14366            // GENERATED ALWAYS AS ROW START|END [HIDDEN]
14367            self.skip(); // consume ROW
14368            let start = if self.match_token(TokenType::Start) {
14369                true
14370            } else {
14371                self.expect(TokenType::End)?;
14372                false
14373            };
14374            let hidden = self.match_identifier("HIDDEN");
14375            col_def
14376                .constraints
14377                .push(ColumnConstraint::GeneratedAsRow(GeneratedAsRow {
14378                    start,
14379                    hidden,
14380                }));
14381            col_def
14382                .constraint_order
14383                .push(ConstraintType::GeneratedAsRow);
14384        } else if self.check(TokenType::Identity) {
14385            // GENERATED [BY DEFAULT | ALWAYS] AS IDENTITY [(...)]
14386            self.skip(); // consume IDENTITY
14387
14388            let mut start = None;
14389            let mut increment = None;
14390            let mut minvalue = None;
14391            let mut maxvalue = None;
14392            let mut cycle = None;
14393
14394            // Optional sequence options in parentheses
14395            if self.match_token(TokenType::LParen) {
14396                loop {
14397                    if self.match_token(TokenType::Start) {
14398                        self.match_token(TokenType::With);
14399                        start = Some(Box::new(self.parse_unary()?));
14400                    } else if self.match_token(TokenType::Increment) {
14401                        self.match_token(TokenType::By);
14402                        increment = Some(Box::new(self.parse_unary()?));
14403                    } else if self.match_token(TokenType::Minvalue) {
14404                        minvalue = Some(Box::new(self.parse_unary()?));
14405                    } else if self.match_token(TokenType::Maxvalue) {
14406                        maxvalue = Some(Box::new(self.parse_unary()?));
14407                    } else if self.match_token(TokenType::Cycle) {
14408                        cycle = Some(true);
14409                    } else if self.match_keywords(&[TokenType::No, TokenType::Cycle]) {
14410                        cycle = Some(false);
14411                    } else if self.check(TokenType::RParen) {
14412                        break;
14413                    } else {
14414                        self.skip();
14415                    }
14416                }
14417                self.expect(TokenType::RParen)?;
14418            }
14419
14420            col_def
14421                .constraints
14422                .push(ColumnConstraint::GeneratedAsIdentity(GeneratedAsIdentity {
14423                    always,
14424                    on_null,
14425                    start,
14426                    increment,
14427                    minvalue,
14428                    maxvalue,
14429                    cycle,
14430                }));
14431            col_def
14432                .constraint_order
14433                .push(ConstraintType::GeneratedAsIdentity);
14434        } else if self.check(TokenType::LParen) {
14435            // GENERATED ALWAYS AS (expr) [STORED|VIRTUAL]
14436            self.skip(); // consume LParen
14437            let expr = self.parse_expression()?;
14438            self.expect(TokenType::RParen)?;
14439
14440            // Check for STORED or VIRTUAL
14441            let (persisted, persistence_kind) = if self.match_identifier("STORED") {
14442                (true, Some("STORED".to_string()))
14443            } else if self.match_identifier("VIRTUAL") {
14444                (false, Some("VIRTUAL".to_string()))
14445            } else {
14446                (false, None)
14447            };
14448
14449            col_def
14450                .constraints
14451                .push(ColumnConstraint::ComputedColumn(ComputedColumn {
14452                    expression: Box::new(expr),
14453                    persisted,
14454                    not_null: false,
14455                    persistence_kind,
14456                    data_type: None,
14457                }));
14458            col_def
14459                .constraint_order
14460                .push(ConstraintType::ComputedColumn);
14461        } else {
14462            // Fallback: treat as GENERATED AS IDENTITY without explicit IDENTITY keyword
14463            col_def
14464                .constraints
14465                .push(ColumnConstraint::GeneratedAsIdentity(GeneratedAsIdentity {
14466                    always,
14467                    on_null,
14468                    start: None,
14469                    increment: None,
14470                    minvalue: None,
14471                    maxvalue: None,
14472                    cycle: None,
14473                }));
14474            col_def
14475                .constraint_order
14476                .push(ConstraintType::GeneratedAsIdentity);
14477        }
14478        Ok(())
14479    }
14480
14481    /// Parse AS (expr) [STORED|VIRTUAL|PERSISTED] [TYPE] [NOT NULL] for computed columns.
14482    /// Called after AS token has been consumed and we've confirmed LParen follows.
14483    /// SingleStore: AS (expr) PERSISTED TYPE NOT NULL
14484    fn parse_as_computed_column(&mut self, col_def: &mut ColumnDef) -> Result<()> {
14485        self.expect(TokenType::LParen)?;
14486        let expr = self.parse_expression()?;
14487        self.expect(TokenType::RParen)?;
14488
14489        // Check for STORED, VIRTUAL, or PERSISTED
14490        let (persisted, persistence_kind) = if self.match_identifier("STORED") {
14491            (true, Some("STORED".to_string()))
14492        } else if self.match_identifier("VIRTUAL") {
14493            (false, Some("VIRTUAL".to_string()))
14494        } else if self.match_identifier("PERSISTED") {
14495            (true, Some("PERSISTED".to_string()))
14496        } else {
14497            (false, None)
14498        };
14499
14500        // For PERSISTED columns, check for optional data type (SingleStore: PERSISTED TYPE NOT NULL)
14501        // Also check for AUTO keyword for SingleStore: PERSISTED AUTO NOT NULL
14502        let data_type = if persistence_kind.as_deref() == Some("PERSISTED") {
14503            // Check if next token looks like a data type (not NOT, not end of input, not comma/rparen)
14504            if !self.is_at_end()
14505                && !self.check(TokenType::Not)
14506                && !self.check(TokenType::Comma)
14507                && !self.check(TokenType::RParen)
14508                && !self.check(TokenType::Semicolon)
14509            {
14510                let tok = self.peek();
14511                // Check for AUTO keyword (SingleStore: PERSISTED AUTO)
14512                if tok.text.eq_ignore_ascii_case("AUTO") {
14513                    self.skip(); // consume AUTO
14514                    None // AUTO is not a data type, just a modifier
14515                } else if tok.token_type.is_keyword()
14516                    || tok.token_type == TokenType::Identifier
14517                    || tok.token_type == TokenType::Var
14518                {
14519                    Some(self.parse_data_type()?)
14520                } else {
14521                    None
14522                }
14523            } else {
14524                None
14525            }
14526        } else {
14527            None
14528        };
14529
14530        // For PERSISTED columns, check for NOT NULL
14531        let not_null = if persistence_kind.as_deref() == Some("PERSISTED") {
14532            self.match_keywords(&[TokenType::Not, TokenType::Null])
14533        } else {
14534            false
14535        };
14536
14537        col_def
14538            .constraints
14539            .push(ColumnConstraint::ComputedColumn(ComputedColumn {
14540                expression: Box::new(expr),
14541                persisted,
14542                not_null,
14543                persistence_kind,
14544                data_type,
14545            }));
14546        col_def
14547            .constraint_order
14548            .push(ConstraintType::ComputedColumn);
14549        Ok(())
14550    }
14551
14552    /// Parse PERIOD FOR SYSTEM_TIME (start_col, end_col) as a table constraint.
14553    /// Returns None if this is not actually PERIOD FOR SYSTEM_TIME (e.g., just a column named PERIOD).
14554    fn parse_period_for_system_time_table_constraint(&mut self) -> Result<Option<TableConstraint>> {
14555        // Save position for possible retreat
14556        let saved = self.current;
14557
14558        if self.match_identifier("PERIOD") {
14559            // Check if followed by FOR SYSTEM_TIME
14560            if self.match_token(TokenType::For) {
14561                if self.match_identifier("SYSTEM_TIME") {
14562                    // Parse (start_col, end_col)
14563                    self.expect(TokenType::LParen)?;
14564                    let start_name = self.expect_identifier_or_safe_keyword_with_quoted()?;
14565                    self.expect(TokenType::Comma)?;
14566                    let end_name = self.expect_identifier_or_safe_keyword_with_quoted()?;
14567                    self.expect(TokenType::RParen)?;
14568                    return Ok(Some(TableConstraint::PeriodForSystemTime {
14569                        start_col: start_name,
14570                        end_col: end_name,
14571                    }));
14572                }
14573            }
14574        }
14575
14576        // Not PERIOD FOR SYSTEM_TIME, retreat
14577        self.current = saved;
14578        Ok(None)
14579    }
14580
14581    /// Parse MySQL table options that appear after the closing paren of column definitions.
14582    /// Handles ENGINE=val, AUTO_INCREMENT=val, DEFAULT CHARSET=val, ROW_FORMAT=val,
14583    /// COMMENT='val', COLLATE=val, etc.
14584    fn parse_mysql_table_options(&mut self) -> Vec<(String, String)> {
14585        let mut options = Vec::new();
14586        loop {
14587            // Skip optional commas between options
14588            self.match_token(TokenType::Comma);
14589
14590            // DEFAULT CHARSET=val or DEFAULT CHARACTER SET=val
14591            if self.check(TokenType::Default) {
14592                let saved = self.current;
14593                self.skip(); // consume DEFAULT
14594                if self.check_identifier("CHARSET") || self.check_identifier("CHARACTER") {
14595                    let is_character = self.check_identifier("CHARACTER");
14596                    let key_part = self.advance().text.to_ascii_uppercase();
14597                    if is_character {
14598                        // CHARACTER SET
14599                        self.match_token(TokenType::Set);
14600                    }
14601                    if self.match_token(TokenType::Eq) {
14602                        let value = if self.check(TokenType::String) {
14603                            let v = format!("'{}'", self.peek().text);
14604                            self.skip();
14605                            v
14606                        } else if self.is_identifier_token()
14607                            || self.is_safe_keyword_as_identifier()
14608                            || self.check(TokenType::Number)
14609                        {
14610                            self.advance().text
14611                        } else {
14612                            self.current = saved;
14613                            break;
14614                        };
14615                        // Normalize CHARSET -> CHARACTER SET
14616                        let key = if is_character || key_part == "CHARSET" {
14617                            "DEFAULT CHARACTER SET".to_string()
14618                        } else {
14619                            format!("DEFAULT {}", key_part)
14620                        };
14621                        options.push((key, value));
14622                        continue;
14623                    }
14624                }
14625                self.current = saved;
14626                break;
14627            }
14628
14629            // ENGINE=val, AUTO_INCREMENT=val, ROW_FORMAT=val, COLLATE=val, KEY_BLOCK_SIZE=val
14630            let is_known_option = self.check_identifier("ENGINE")
14631                || self.check(TokenType::AutoIncrement)
14632                || self.check_identifier("ROW_FORMAT")
14633                || self.check(TokenType::Collate)
14634                || self.check_identifier("KEY_BLOCK_SIZE")
14635                || self.check_identifier("PACK_KEYS")
14636                || self.check_identifier("STATS_AUTO_RECALC")
14637                || self.check_identifier("STATS_PERSISTENT")
14638                || self.check_identifier("STATS_SAMPLE_PAGES")
14639                || self.check_identifier("MAX_ROWS")
14640                || self.check_identifier("MIN_ROWS")
14641                || self.check_identifier("CHECKSUM")
14642                || self.check_identifier("DELAY_KEY_WRITE")
14643                || self.check_identifier("COMPRESSION")
14644                || self.check_identifier("CONNECTION")
14645                || self.check_identifier("TABLESPACE")
14646                || self.check_identifier("ENCRYPTION");
14647
14648            if is_known_option {
14649                let key = self.advance().text.to_ascii_uppercase();
14650                if self.match_token(TokenType::Eq) {
14651                    let value = if self.check(TokenType::String) {
14652                        let v = format!("'{}'", self.peek().text);
14653                        self.skip();
14654                        v
14655                    } else if self.check(TokenType::Number) {
14656                        self.advance().text
14657                    } else if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
14658                        self.advance().text
14659                    } else {
14660                        break;
14661                    };
14662                    options.push((key, value));
14663                    continue;
14664                }
14665                break;
14666            }
14667
14668            // COMMENT='val' (Comment is a keyword token type)
14669            if self.check(TokenType::Comment) {
14670                let saved = self.current;
14671                self.skip(); // consume COMMENT
14672                if self.match_token(TokenType::Eq) {
14673                    if self.check(TokenType::String) {
14674                        let v = format!("'{}'", self.peek().text);
14675                        self.skip();
14676                        options.push(("COMMENT".to_string(), v));
14677                        continue;
14678                    }
14679                } else if self.check(TokenType::String) {
14680                    let v = format!("'{}'", self.peek().text);
14681                    self.skip();
14682                    options.push(("COMMENT".to_string(), v));
14683                    continue;
14684                }
14685                self.current = saved;
14686                break;
14687            }
14688
14689            // CHARACTER SET=val or CHARSET=val (without DEFAULT prefix)
14690            if self.check_identifier("CHARACTER") || self.check_identifier("CHARSET") {
14691                let saved = self.current;
14692                let is_character = self.check_identifier("CHARACTER");
14693                self.skip(); // consume CHARACTER or CHARSET
14694                if is_character {
14695                    // CHARACTER SET
14696                    if !self.match_token(TokenType::Set) {
14697                        self.current = saved;
14698                        break;
14699                    }
14700                }
14701                if self.match_token(TokenType::Eq) {
14702                    let value = if self.check(TokenType::String) {
14703                        let v = format!("'{}'", self.peek().text);
14704                        self.skip();
14705                        v
14706                    } else if self.is_identifier_token()
14707                        || self.is_safe_keyword_as_identifier()
14708                        || self.check(TokenType::Number)
14709                    {
14710                        self.advance().text
14711                    } else {
14712                        self.current = saved;
14713                        break;
14714                    };
14715                    options.push(("CHARACTER SET".to_string(), value));
14716                    continue;
14717                }
14718                self.current = saved;
14719                break;
14720            }
14721
14722            break;
14723        }
14724        options
14725    }
14726
14727    /// Parse Hive-specific table properties that appear after column definitions.
14728    /// Handles: ROW FORMAT (SERDE/DELIMITED), STORED AS/BY, LOCATION, TBLPROPERTIES
14729    fn parse_hive_table_properties(&mut self) -> Result<Vec<Expression>> {
14730        let mut properties = Vec::new();
14731
14732        loop {
14733            // ROW FORMAT SERDE 'class' [WITH SERDEPROPERTIES (...)]
14734            // ROW FORMAT DELIMITED [FIELDS TERMINATED BY ...] [...]
14735            if self.match_token(TokenType::Row) {
14736                if let Some(row_format) = self.parse_row()? {
14737                    properties.push(row_format);
14738                    continue;
14739                }
14740            }
14741
14742            // STORED AS INPUTFORMAT 'input' OUTPUTFORMAT 'output'
14743            // STORED AS format_name
14744            // STORED BY 'storage_handler_class'
14745            if self.match_identifier("STORED") {
14746                if self.match_token(TokenType::By) {
14747                    // STORED BY 'storage_handler_class'
14748                    let handler = self.parse_string()?.unwrap_or(Expression::Null(Null));
14749                    properties.push(Expression::StorageHandlerProperty(Box::new(
14750                        StorageHandlerProperty {
14751                            this: Box::new(handler),
14752                        },
14753                    )));
14754                    continue;
14755                } else if self.match_token(TokenType::As) {
14756                    // STORED AS INPUTFORMAT 'x' OUTPUTFORMAT 'y' or STORED AS format
14757                    if self.match_token(TokenType::InputFormat) {
14758                        let input_format = self.parse_string()?;
14759                        let output_format = if self.match_identifier("OUTPUTFORMAT") {
14760                            self.parse_string()?
14761                        } else {
14762                            None
14763                        };
14764                        // Use InputOutputFormat inside FileFormatProperty.this
14765                        let io_format =
14766                            Expression::InputOutputFormat(Box::new(InputOutputFormat {
14767                                input_format: input_format.map(Box::new),
14768                                output_format: output_format.map(Box::new),
14769                            }));
14770                        properties.push(Expression::FileFormatProperty(Box::new(
14771                            FileFormatProperty {
14772                                this: Some(Box::new(io_format)),
14773                                expressions: vec![],
14774                                hive_format: Some(Box::new(Expression::Boolean(BooleanLiteral {
14775                                    value: true,
14776                                }))),
14777                            },
14778                        )));
14779                        continue;
14780                    } else {
14781                        // STORED AS format_name (e.g., STORED AS TEXTFILE, STORED AS ORC)
14782                        let format = if self.check(TokenType::String) {
14783                            Expression::Literal(Literal::String(self.advance().text.clone()))
14784                        } else if self.is_identifier_token() || self.is_safe_keyword_as_identifier()
14785                        {
14786                            Expression::Identifier(Identifier::new(self.advance().text.clone()))
14787                        } else {
14788                            break;
14789                        };
14790                        properties.push(Expression::FileFormatProperty(Box::new(
14791                            FileFormatProperty {
14792                                this: Some(Box::new(format)),
14793                                expressions: vec![],
14794                                hive_format: Some(Box::new(Expression::Boolean(BooleanLiteral {
14795                                    value: true,
14796                                }))),
14797                            },
14798                        )));
14799                        continue;
14800                    }
14801                }
14802            }
14803
14804            // USING format_name (Databricks/Spark) e.g., USING DELTA, USING PARQUET
14805            // This is similar to STORED AS but uses different syntax
14806            if self.match_token(TokenType::Using) {
14807                // Parse the format name (e.g., DELTA, PARQUET, ICEBERG, etc.)
14808                let format = if self.check(TokenType::String) {
14809                    Expression::Literal(Literal::String(self.advance().text.clone()))
14810                } else if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
14811                    Expression::Identifier(Identifier::new(self.advance().text.clone()))
14812                } else {
14813                    break;
14814                };
14815                // Create FileFormatProperty WITHOUT hive_format to signal USING syntax
14816                properties.push(Expression::FileFormatProperty(Box::new(
14817                    FileFormatProperty {
14818                        this: Some(Box::new(format)),
14819                        expressions: vec![],
14820                        hive_format: None, // None indicates USING syntax (not STORED AS)
14821                    },
14822                )));
14823                continue;
14824            }
14825
14826            // LOCATION 'path'
14827            if self.match_identifier("LOCATION") {
14828                let path = self.parse_string()?.unwrap_or(Expression::Null(Null));
14829                properties.push(Expression::LocationProperty(Box::new(LocationProperty {
14830                    this: Box::new(path),
14831                })));
14832                continue;
14833            }
14834
14835            // TBLPROPERTIES ('key'='value', ...)
14836            if self.match_identifier("TBLPROPERTIES") {
14837                // Parse the property list manually since parse_property doesn't handle key=value
14838                self.expect(TokenType::LParen)?;
14839                let mut prop_exprs = Vec::new();
14840                loop {
14841                    if self.check(TokenType::RParen) {
14842                        break;
14843                    }
14844                    // Parse 'key'='value' or key=value
14845                    let key = self.parse_primary()?;
14846                    if self.match_token(TokenType::Eq) {
14847                        let value = self.parse_primary()?;
14848                        prop_exprs.push(Expression::Eq(Box::new(BinaryOp::new(key, value))));
14849                    } else {
14850                        prop_exprs.push(key);
14851                    }
14852                    if !self.match_token(TokenType::Comma) {
14853                        break;
14854                    }
14855                }
14856                self.expect(TokenType::RParen)?;
14857                properties.push(Expression::Properties(Box::new(Properties {
14858                    expressions: prop_exprs,
14859                })));
14860                continue;
14861            }
14862
14863            // DISTRIBUTED BY HASH (col1, col2) [BUCKETS n] (StarRocks/Doris)
14864            if self.match_identifier("DISTRIBUTED") {
14865                if let Some(dist_prop) = self.parse_distributed_property()? {
14866                    properties.push(dist_prop);
14867                    continue;
14868                }
14869            }
14870
14871            // CLUSTERED BY (col, col, ...) [SORTED BY (col, col, ...)] INTO n BUCKETS (Hive/Athena)
14872            if self.match_identifier("CLUSTERED") {
14873                self.expect(TokenType::By)?;
14874                self.expect(TokenType::LParen)?;
14875                let expressions = self.parse_expression_list()?;
14876                self.expect(TokenType::RParen)?;
14877
14878                // Optional SORTED BY (col, col, ...)
14879                let sorted_by = if self.match_identifier("SORTED") {
14880                    self.expect(TokenType::By)?;
14881                    self.expect(TokenType::LParen)?;
14882                    let sorted_exprs = self.parse_expression_list()?;
14883                    self.expect(TokenType::RParen)?;
14884                    Some(Box::new(Expression::Tuple(Box::new(Tuple {
14885                        expressions: sorted_exprs,
14886                    }))))
14887                } else {
14888                    None
14889                };
14890
14891                // INTO n BUCKETS
14892                let buckets = if self.match_token(TokenType::Into) {
14893                    let num = self.parse_expression()?;
14894                    if !self.match_identifier("BUCKETS") {
14895                        return Err(self.parse_error("Expected BUCKETS after INTO <n>"));
14896                    }
14897                    Some(Box::new(num))
14898                } else {
14899                    None
14900                };
14901
14902                properties.push(Expression::ClusteredByProperty(Box::new(
14903                    ClusteredByProperty {
14904                        expressions,
14905                        sorted_by,
14906                        buckets,
14907                    },
14908                )));
14909                continue;
14910            }
14911
14912            // PARTITIONED BY (col, col, ...) or PARTITIONED BY (col, BUCKET(n, col), ...) (Hive/Athena/Iceberg)
14913            if self.match_identifier("PARTITIONED") {
14914                self.expect(TokenType::By)?;
14915                self.expect(TokenType::LParen)?;
14916
14917                let mut partition_exprs = Vec::new();
14918                loop {
14919                    if self.check(TokenType::RParen) {
14920                        break;
14921                    }
14922
14923                    // Check for transform functions like BUCKET(n, col), TRUNCATE(n, col), etc.
14924                    if self.check_identifier("BUCKET") || self.check_identifier("TRUNCATE") {
14925                        let func_name = self.advance().text.clone();
14926                        self.expect(TokenType::LParen)?;
14927                        let args = self.parse_expression_list()?;
14928                        self.expect(TokenType::RParen)?;
14929
14930                        // Create a Function expression for BUCKET/TRUNCATE
14931                        partition_exprs.push(Expression::Function(Box::new(Function {
14932                            name: func_name,
14933                            args,
14934                            distinct: false,
14935                            trailing_comments: Vec::new(),
14936                            use_bracket_syntax: false,
14937                            no_parens: false,
14938                            quoted: false,
14939                            span: None,
14940                            inferred_type: None,
14941                        })));
14942                    } else {
14943                        // Try to parse as column definition (name data_type) for Hive-style partitioned by
14944                        // e.g., PARTITIONED BY (y INT, z STRING)
14945                        let saved_pos = self.current;
14946                        let mut parsed_as_column = false;
14947                        // Allow type keywords (like DATE, TIMESTAMP) as column names in PARTITIONED BY
14948                        if self.check(TokenType::Var)
14949                            || self.check(TokenType::Identifier)
14950                            || self.check(TokenType::Date)
14951                            || self.check(TokenType::Timestamp)
14952                            || self.check(TokenType::Int)
14953                            || self.check(TokenType::BigInt)
14954                            || self.check(TokenType::SmallInt)
14955                            || self.check(TokenType::TinyInt)
14956                            || self.check(TokenType::Float)
14957                            || self.check(TokenType::Double)
14958                            || self.check(TokenType::Boolean)
14959                        {
14960                            let col_name = self.advance().text.clone();
14961                            // Check if next token looks like a data type
14962                            if self.check(TokenType::Var)
14963                                || self.check(TokenType::Identifier)
14964                                || self.check(TokenType::Int)
14965                                || self.check(TokenType::BigInt)
14966                                || self.check(TokenType::SmallInt)
14967                                || self.check(TokenType::TinyInt)
14968                                || self.check(TokenType::Float)
14969                                || self.check(TokenType::Double)
14970                                || self.check(TokenType::Boolean)
14971                                || self.check(TokenType::Date)
14972                                || self.check(TokenType::Timestamp)
14973                            {
14974                                let type_text = self.peek().text.to_ascii_uppercase();
14975                                let is_type = matches!(
14976                                    type_text.as_str(),
14977                                    "INT"
14978                                        | "INTEGER"
14979                                        | "BIGINT"
14980                                        | "SMALLINT"
14981                                        | "TINYINT"
14982                                        | "FLOAT"
14983                                        | "DOUBLE"
14984                                        | "DECIMAL"
14985                                        | "NUMERIC"
14986                                        | "STRING"
14987                                        | "VARCHAR"
14988                                        | "CHAR"
14989                                        | "BINARY"
14990                                        | "BOOLEAN"
14991                                        | "DATE"
14992                                        | "TIMESTAMP"
14993                                        | "DATETIME"
14994                                        | "ARRAY"
14995                                        | "MAP"
14996                                        | "STRUCT"
14997                                );
14998                                if is_type {
14999                                    // Parse as column definition
15000                                    let data_type = self.parse_data_type()?;
15001                                    // Store as ColumnDef expression
15002                                    partition_exprs.push(Expression::ColumnDef(Box::new(
15003                                        crate::expressions::ColumnDef::new(col_name, data_type),
15004                                    )));
15005                                    parsed_as_column = true;
15006                                }
15007                            }
15008                        }
15009                        if !parsed_as_column {
15010                            // Backtrack and parse as regular expression
15011                            self.current = saved_pos;
15012                            partition_exprs.push(self.parse_expression()?);
15013                        }
15014                    }
15015
15016                    if !self.match_token(TokenType::Comma) {
15017                        break;
15018                    }
15019                }
15020                self.expect(TokenType::RParen)?;
15021
15022                properties.push(Expression::PartitionedByProperty(Box::new(
15023                    PartitionedByProperty {
15024                        this: Box::new(Expression::Tuple(Box::new(Tuple {
15025                            expressions: partition_exprs,
15026                        }))),
15027                    },
15028                )));
15029                continue;
15030            }
15031
15032            // No more Hive properties
15033            break;
15034        }
15035
15036        Ok(properties)
15037    }
15038
15039    /// Parse table-level properties that appear after the closing paren of column definitions.
15040    /// Currently handles TSQL WITH(SYSTEM_VERSIONING=ON(...)).
15041    fn parse_post_table_properties(&mut self) -> Result<Vec<Expression>> {
15042        let mut properties = Vec::new();
15043
15044        // Doris/StarRocks: UNIQUE KEY (cols) or DUPLICATE KEY (cols) after column definitions
15045        // These are table key properties that define the distribution/sort key
15046        let is_doris_starrocks = matches!(
15047            self.config.dialect,
15048            Some(crate::dialects::DialectType::Doris)
15049                | Some(crate::dialects::DialectType::StarRocks)
15050        );
15051        if is_doris_starrocks {
15052            // UNIQUE KEY (c1, c2, ...) - defines unique key columns
15053            if self.match_text_seq(&["UNIQUE", "KEY"]) {
15054                let exprs = self.parse_composite_key_expressions()?;
15055                properties.push(Expression::UniqueKeyProperty(Box::new(
15056                    crate::expressions::UniqueKeyProperty { expressions: exprs },
15057                )));
15058            }
15059            // DUPLICATE KEY (c1, c2, ...) - defines duplicate key columns
15060            else if self.match_text_seq(&["DUPLICATE", "KEY"]) {
15061                let exprs = self.parse_composite_key_expressions()?;
15062                properties.push(Expression::DuplicateKeyProperty(Box::new(
15063                    crate::expressions::DuplicateKeyProperty { expressions: exprs },
15064                )));
15065            }
15066
15067            // DISTRIBUTED BY HASH (col1, col2) [BUCKETS n] - comes after UNIQUE KEY / DUPLICATE KEY
15068            if self.match_identifier("DISTRIBUTED") {
15069                if let Some(dist_prop) = self.parse_distributed_property()? {
15070                    properties.push(dist_prop);
15071                }
15072            }
15073
15074            // PROPERTIES ('key'='value', ...) - comes after DISTRIBUTED BY
15075            if self.match_identifier("PROPERTIES") {
15076                let props = self.parse_options_list()?;
15077                if !props.is_empty() {
15078                    properties.push(Expression::Properties(Box::new(Properties {
15079                        expressions: props,
15080                    })));
15081                }
15082            }
15083        }
15084
15085        // Check for WITH( that might contain SYSTEM_VERSIONING
15086        // We need to be careful not to consume a WITH that is meant for WITH properties
15087        // or other purposes. We only handle WITH(SYSTEM_VERSIONING=...) here.
15088        if self.check(TokenType::With) {
15089            // Look ahead: WITH followed by ( followed by SYSTEM_VERSIONING
15090            let saved = self.current;
15091            if self.match_token(TokenType::With) {
15092                if self.match_token(TokenType::LParen) {
15093                    if self.check_identifier("SYSTEM_VERSIONING") {
15094                        self.skip(); // consume SYSTEM_VERSIONING
15095                        self.expect(TokenType::Eq)?;
15096
15097                        let on = if self.match_token(TokenType::On) {
15098                            true
15099                        } else if self.match_identifier("OFF") {
15100                            false
15101                        } else {
15102                            return Err(
15103                                self.parse_error("Expected ON or OFF after SYSTEM_VERSIONING=")
15104                            );
15105                        };
15106
15107                        let mut history_table = None;
15108                        let mut data_consistency = None;
15109
15110                        // Optional parameters: ON(HISTORY_TABLE=..., DATA_CONSISTENCY_CHECK=...)
15111                        if on && self.match_token(TokenType::LParen) {
15112                            loop {
15113                                if self.check(TokenType::RParen) {
15114                                    break;
15115                                }
15116                                if self.match_identifier("HISTORY_TABLE") {
15117                                    self.expect(TokenType::Eq)?;
15118                                    // Parse table reference (could be [dbo].[table])
15119                                    let table_ref = self.parse_table_ref()?;
15120                                    history_table = Some(Expression::Table(Box::new(table_ref)));
15121                                } else if self.match_identifier("DATA_CONSISTENCY_CHECK") {
15122                                    self.expect(TokenType::Eq)?;
15123                                    let val = self.expect_identifier_or_keyword()?;
15124                                    data_consistency = Some(Expression::Identifier(
15125                                        crate::expressions::Identifier::new(val),
15126                                    ));
15127                                } else if self.check(TokenType::RParen) {
15128                                    break;
15129                                } else {
15130                                    self.skip();
15131                                }
15132                                self.match_token(TokenType::Comma);
15133                            }
15134                            self.expect(TokenType::RParen)?;
15135                        }
15136
15137                        self.expect(TokenType::RParen)?; // close WITH(...)
15138
15139                        properties.push(Expression::WithSystemVersioningProperty(Box::new(
15140                            WithSystemVersioningProperty {
15141                                on: if on {
15142                                    Some(Box::new(Expression::Boolean(
15143                                        crate::expressions::BooleanLiteral { value: true },
15144                                    )))
15145                                } else {
15146                                    None
15147                                },
15148                                this: history_table.map(Box::new),
15149                                data_consistency: data_consistency.map(Box::new),
15150                                retention_period: None,
15151                                with_: Some(Box::new(Expression::Boolean(
15152                                    crate::expressions::BooleanLiteral { value: true },
15153                                ))),
15154                            },
15155                        )));
15156                    } else {
15157                        // Not SYSTEM_VERSIONING, retreat
15158                        self.current = saved;
15159                    }
15160                } else {
15161                    // Not WITH(...), retreat
15162                    self.current = saved;
15163                }
15164            }
15165        }
15166
15167        Ok(properties)
15168    }
15169
15170    /// Parse composite key expressions for UNIQUE KEY (cols) or DUPLICATE KEY (cols)
15171    /// Returns a vector of column identifiers
15172    fn parse_composite_key_expressions(&mut self) -> Result<Vec<Expression>> {
15173        self.expect(TokenType::LParen)?;
15174        let mut expressions = Vec::new();
15175        loop {
15176            if let Some(id) = self.parse_id_var()? {
15177                expressions.push(id);
15178            } else {
15179                break;
15180            }
15181            if !self.match_token(TokenType::Comma) {
15182                break;
15183            }
15184        }
15185        self.expect(TokenType::RParen)?;
15186        Ok(expressions)
15187    }
15188
15189    /// Parse a table-level constraint
15190    fn parse_table_constraint(&mut self) -> Result<TableConstraint> {
15191        // Optional constraint name
15192        let name = if self.match_token(TokenType::Constraint) {
15193            // Use safe keyword version to accept keywords as constraint names (e.g., CONSTRAINT identity CHECK ...)
15194            Some(self.expect_identifier_or_safe_keyword_with_quoted()?)
15195        } else {
15196            None
15197        };
15198
15199        self.parse_constraint_definition(name)
15200    }
15201
15202    /// Parse constraint definition (after optional CONSTRAINT name)
15203    fn parse_constraint_definition(&mut self, name: Option<Identifier>) -> Result<TableConstraint> {
15204        if self.match_keywords(&[TokenType::PrimaryKey, TokenType::Key]) {
15205            // PRIMARY KEY [CLUSTERED|NONCLUSTERED] [name] (col1, col2) [INCLUDE (col3, col4)]
15206            // MySQL allows: PRIMARY KEY pk_name (col1, col2)
15207            // TSQL allows: PRIMARY KEY CLUSTERED (col1, col2)
15208
15209            // Check for TSQL CLUSTERED/NONCLUSTERED modifier
15210            let clustered = if self.check_identifier("CLUSTERED") {
15211                self.skip();
15212                Some("CLUSTERED".to_string())
15213            } else if self.check_identifier("NONCLUSTERED") {
15214                self.skip();
15215                Some("NONCLUSTERED".to_string())
15216            } else {
15217                None
15218            };
15219
15220            let actual_name = if name.is_none() && !self.check(TokenType::LParen) {
15221                if matches!(
15222                    self.config.dialect,
15223                    Some(crate::dialects::DialectType::ClickHouse)
15224                ) {
15225                    // ClickHouse: PRIMARY KEY col (without parentheses)
15226                    None
15227                } else if self.is_identifier_token() || self.check(TokenType::QuotedIdentifier) {
15228                    Some(self.expect_identifier_with_quoted()?)
15229                } else if self.check(TokenType::String)
15230                    && matches!(
15231                        self.config.dialect,
15232                        Some(crate::dialects::DialectType::MySQL)
15233                    )
15234                {
15235                    // MySQL: double-quoted strings can be used as constraint names
15236                    // e.g., PRIMARY KEY "pk_name" (id) -> PRIMARY KEY `pk_name` (id)
15237                    let s = self.advance().text.clone();
15238                    Some(Identifier {
15239                        name: s,
15240                        quoted: true,
15241                        trailing_comments: Vec::new(),
15242                        span: None,
15243                    })
15244                } else {
15245                    None
15246                }
15247            } else {
15248                name.clone()
15249            };
15250            // ClickHouse: PRIMARY KEY col without parens — parse single column
15251            let columns = if matches!(
15252                self.config.dialect,
15253                Some(crate::dialects::DialectType::ClickHouse)
15254            ) && !self.check(TokenType::LParen)
15255                && (self.is_identifier_token() || self.is_safe_keyword_as_identifier())
15256            {
15257                let col_name = self.expect_identifier_or_keyword_with_quoted()?;
15258                vec![col_name]
15259            } else {
15260                self.expect(TokenType::LParen)?;
15261                // ClickHouse: allow empty PRIMARY KEY ()
15262                let cols = if self.check(TokenType::RParen) {
15263                    Vec::new()
15264                } else if matches!(
15265                    self.config.dialect,
15266                    Some(crate::dialects::DialectType::ClickHouse)
15267                ) {
15268                    // ClickHouse: PRIMARY KEY(v1, gcd(v1, v2)) - expressions allowed
15269                    let mut exprs = Vec::new();
15270                    loop {
15271                        let expr = self.parse_expression()?;
15272                        let name = self.expression_to_sql(&expr);
15273                        exprs.push(Identifier::new(name));
15274                        if !self.match_token(TokenType::Comma) {
15275                            break;
15276                        }
15277                    }
15278                    exprs
15279                } else {
15280                    self.parse_index_identifier_list()?
15281                };
15282                self.expect(TokenType::RParen)?;
15283                cols
15284            };
15285            // Parse optional INCLUDE (columns)
15286            let include_columns = if self.match_identifier("INCLUDE") {
15287                self.expect(TokenType::LParen)?;
15288                let cols = self.parse_identifier_list()?;
15289                self.expect(TokenType::RParen)?;
15290                cols
15291            } else {
15292                Vec::new()
15293            };
15294            // Parse optional constraint modifiers (ENFORCED, DEFERRABLE, etc.)
15295            let mut modifiers = self.parse_constraint_modifiers();
15296            modifiers.clustered = clustered;
15297            let has_constraint_keyword = name.is_some();
15298            Ok(TableConstraint::PrimaryKey {
15299                name: actual_name.or(name),
15300                columns,
15301                include_columns,
15302                modifiers,
15303                has_constraint_keyword,
15304            })
15305        } else if self.match_token(TokenType::Unique) {
15306            // UNIQUE [CLUSTERED|NONCLUSTERED] [KEY|INDEX] [NULLS NOT DISTINCT] [name] (col1, col2) or UNIQUE column_name
15307            // MySQL allows: UNIQUE KEY name (cols), UNIQUE INDEX name (cols), UNIQUE (cols)
15308            // TSQL allows: UNIQUE CLUSTERED (cols)
15309            // PostgreSQL 15+: UNIQUE NULLS NOT DISTINCT (cols)
15310
15311            // Check for TSQL CLUSTERED/NONCLUSTERED modifier
15312            let clustered = if self.check_identifier("CLUSTERED") {
15313                self.skip();
15314                Some("CLUSTERED".to_string())
15315            } else if self.check_identifier("NONCLUSTERED") {
15316                self.skip();
15317                Some("NONCLUSTERED".to_string())
15318            } else {
15319                None
15320            };
15321
15322            let use_key_keyword =
15323                self.match_token(TokenType::Key) || self.match_token(TokenType::Index);
15324
15325            // Check for NULLS NOT DISTINCT (PostgreSQL 15+ feature)
15326            let nulls_not_distinct = self.match_text_seq(&["NULLS", "NOT", "DISTINCT"]);
15327
15328            // Check for optional constraint name (before columns)
15329            let actual_name = if name.is_none()
15330                && self.is_identifier_token()
15331                && !self.check_next(TokenType::Comma)
15332            {
15333                // Name might be here: UNIQUE KEY idx_name (cols)
15334                if self.check_next(TokenType::LParen) {
15335                    Some(self.expect_identifier_with_quoted()?)
15336                } else {
15337                    None
15338                }
15339            } else {
15340                name.clone()
15341            };
15342
15343            if self.match_token(TokenType::LParen) {
15344                let columns = self.parse_index_identifier_list()?;
15345                self.expect(TokenType::RParen)?;
15346                let mut modifiers = self.parse_constraint_modifiers();
15347                modifiers.clustered = clustered;
15348                if use_key_keyword {
15349                    // UNIQUE KEY/INDEX - use Index constraint type with UNIQUE kind
15350                    Ok(TableConstraint::Index {
15351                        name: actual_name.or(name),
15352                        columns,
15353                        kind: Some("UNIQUE".to_string()),
15354                        modifiers,
15355                        use_key_keyword,
15356                        expression: None,
15357                        index_type: None,
15358                        granularity: None,
15359                    })
15360                } else {
15361                    let has_constraint_keyword = name.is_some();
15362                    Ok(TableConstraint::Unique {
15363                        name: actual_name.or(name),
15364                        columns,
15365                        columns_parenthesized: true,
15366                        modifiers,
15367                        has_constraint_keyword,
15368                        nulls_not_distinct,
15369                    })
15370                }
15371            } else {
15372                // Single column unique (for ALTER TABLE ADD CONSTRAINT name UNIQUE colname)
15373                let col_name = self.expect_identifier()?;
15374                let mut modifiers = self.parse_constraint_modifiers();
15375                modifiers.clustered = clustered;
15376                let has_constraint_keyword = name.is_some();
15377                Ok(TableConstraint::Unique {
15378                    name: actual_name.or(name),
15379                    columns: vec![Identifier::new(col_name)],
15380                    columns_parenthesized: false,
15381                    modifiers,
15382                    has_constraint_keyword,
15383                    nulls_not_distinct,
15384                })
15385            }
15386        } else if self.match_keywords(&[TokenType::ForeignKey, TokenType::Key]) {
15387            // FOREIGN KEY (col1) [REFERENCES other_table(col2)] [ON DELETE ...] [ON UPDATE ...]
15388            self.expect(TokenType::LParen)?;
15389            let columns = self.parse_identifier_list()?;
15390            self.expect(TokenType::RParen)?;
15391            if self.match_token(TokenType::References) {
15392                let references = self.parse_foreign_key_ref()?;
15393                let modifiers = self.parse_constraint_modifiers();
15394                Ok(TableConstraint::ForeignKey {
15395                    name,
15396                    columns,
15397                    references: Some(references),
15398                    on_delete: None,
15399                    on_update: None,
15400                    modifiers,
15401                })
15402            } else {
15403                // No REFERENCES - parse optional ON DELETE/ON UPDATE directly
15404                let mut on_delete = None;
15405                let mut on_update = None;
15406                loop {
15407                    if self.check(TokenType::On) {
15408                        let saved = self.current;
15409                        self.skip(); // consume ON
15410                        if self.match_token(TokenType::Delete) {
15411                            on_delete = Some(self.parse_referential_action()?);
15412                        } else if self.match_token(TokenType::Update) {
15413                            on_update = Some(self.parse_referential_action()?);
15414                        } else {
15415                            self.current = saved;
15416                            break;
15417                        }
15418                    } else {
15419                        break;
15420                    }
15421                }
15422                let modifiers = self.parse_constraint_modifiers();
15423                Ok(TableConstraint::ForeignKey {
15424                    name,
15425                    columns,
15426                    references: None,
15427                    on_delete,
15428                    on_update,
15429                    modifiers,
15430                })
15431            }
15432        } else if self.match_token(TokenType::Check) {
15433            // CHECK (expression) or CHECK (SELECT ...) or ClickHouse: CHECK expression (without parens)
15434            let expression = if self.match_token(TokenType::LParen) {
15435                let expr = if self.check(TokenType::Select) || self.check(TokenType::With) {
15436                    // Subquery in CHECK constraint
15437                    let stmt = self.parse_statement()?;
15438                    Expression::Subquery(Box::new(Subquery {
15439                        this: stmt,
15440                        alias: None,
15441                        column_aliases: Vec::new(),
15442                        order_by: None,
15443                        limit: None,
15444                        offset: None,
15445                        distribute_by: None,
15446                        sort_by: None,
15447                        cluster_by: None,
15448                        lateral: false,
15449                        modifiers_inside: false,
15450                        trailing_comments: Vec::new(),
15451                        inferred_type: None,
15452                    }))
15453                } else {
15454                    self.parse_expression()?
15455                };
15456                self.expect(TokenType::RParen)?;
15457                expr
15458            } else if matches!(
15459                self.config.dialect,
15460                Some(crate::dialects::DialectType::ClickHouse)
15461            ) {
15462                self.parse_or()?
15463            } else {
15464                self.expect(TokenType::LParen)?;
15465                unreachable!()
15466            };
15467            let modifiers = self.parse_constraint_modifiers();
15468            Ok(TableConstraint::Check {
15469                name,
15470                expression,
15471                modifiers,
15472            })
15473        } else if self.match_token(TokenType::Exclude) {
15474            // PostgreSQL EXCLUDE constraint
15475            // EXCLUDE [USING method] (element WITH operator, ...) [INCLUDE (cols)] [WHERE (expr)] [WITH (params)]
15476            let using = if self.match_token(TokenType::Using) {
15477                Some(self.expect_identifier()?)
15478            } else {
15479                None
15480            };
15481
15482            self.expect(TokenType::LParen)?;
15483            let mut elements = Vec::new();
15484            loop {
15485                // Parse element expression: may be a function call like INT4RANGE(vid, nid)
15486                // or column name possibly with operator class, ASC/DESC, NULLS FIRST/LAST
15487                let mut expr_parts = Vec::new();
15488                let mut paren_depth = 0;
15489                while !self.is_at_end() {
15490                    if self.check(TokenType::LParen) {
15491                        paren_depth += 1;
15492                        expr_parts.push(self.advance().text);
15493                    } else if self.check(TokenType::RParen) {
15494                        if paren_depth == 0 {
15495                            break;
15496                        }
15497                        paren_depth -= 1;
15498                        expr_parts.push(self.advance().text);
15499                    } else if paren_depth == 0 && self.check(TokenType::With) {
15500                        break;
15501                    } else if self.check(TokenType::String) {
15502                        // Preserve string literal quotes
15503                        let token = self.advance();
15504                        expr_parts.push(format!("'{}'", token.text));
15505                    } else {
15506                        expr_parts.push(self.advance().text);
15507                    }
15508                }
15509                let expression = expr_parts
15510                    .join(" ")
15511                    .replace(" (", "(")
15512                    .replace(" )", ")")
15513                    .replace("( ", "(")
15514                    .replace(" ,", ",");
15515
15516                // Parse WITH operator
15517                self.expect(TokenType::With)?;
15518                let operator = self.advance().text.clone();
15519
15520                elements.push(ExcludeElement {
15521                    expression,
15522                    operator,
15523                });
15524
15525                if !self.match_token(TokenType::Comma) {
15526                    break;
15527                }
15528            }
15529            self.expect(TokenType::RParen)?;
15530
15531            // Parse optional INCLUDE (columns)
15532            let include_columns = if self.match_identifier("INCLUDE") {
15533                self.expect(TokenType::LParen)?;
15534                let cols = self.parse_identifier_list()?;
15535                self.expect(TokenType::RParen)?;
15536                cols
15537            } else {
15538                Vec::new()
15539            };
15540
15541            // Parse optional WITH (storage_parameters)
15542            let with_params = if self.match_token(TokenType::With) {
15543                self.expect(TokenType::LParen)?;
15544                let mut params = Vec::new();
15545                loop {
15546                    let key = self.expect_identifier()?;
15547                    self.expect(TokenType::Eq)?;
15548                    let val = self.advance().text.clone();
15549                    params.push((key, val));
15550                    if !self.match_token(TokenType::Comma) {
15551                        break;
15552                    }
15553                }
15554                self.expect(TokenType::RParen)?;
15555                params
15556            } else {
15557                Vec::new()
15558            };
15559
15560            // Parse optional USING INDEX TABLESPACE tablespace_name
15561            let using_index_tablespace =
15562                if self.check(TokenType::Using) && self.check_next(TokenType::Index) {
15563                    self.skip(); // consume USING
15564                    self.skip(); // consume INDEX
15565                    if self.match_identifier("TABLESPACE") {
15566                        Some(self.expect_identifier()?)
15567                    } else {
15568                        None
15569                    }
15570                } else {
15571                    None
15572                };
15573
15574            // Parse optional WHERE clause
15575            let where_clause = if self.match_token(TokenType::Where) {
15576                self.expect(TokenType::LParen)?;
15577                let expr = self.parse_expression()?;
15578                self.expect(TokenType::RParen)?;
15579                Some(Box::new(expr))
15580            } else {
15581                None
15582            };
15583
15584            let modifiers = self.parse_constraint_modifiers();
15585            Ok(TableConstraint::Exclude {
15586                name,
15587                using,
15588                elements,
15589                include_columns,
15590                where_clause,
15591                with_params,
15592                using_index_tablespace,
15593                modifiers,
15594            })
15595        } else if matches!(
15596            self.config.dialect,
15597            Some(crate::dialects::DialectType::ClickHouse)
15598        ) && self.check_identifier("ASSUME")
15599        {
15600            // ClickHouse: CONSTRAINT name ASSUME expression
15601            // Used for query optimization assumptions — store as CHECK constraint
15602            self.skip(); // consume ASSUME
15603            let expr = self.parse_expression()?;
15604            Ok(TableConstraint::Check {
15605                name,
15606                expression: expr,
15607                modifiers: Default::default(),
15608            })
15609        } else {
15610            Err(self.parse_error("Expected PRIMARY KEY, UNIQUE, FOREIGN KEY, CHECK, or EXCLUDE"))
15611        }
15612    }
15613
15614    /// Parse INDEX/KEY table constraint for MySQL
15615    /// Syntax: [FULLTEXT|SPATIAL] {INDEX|KEY} [name] [USING {BTREE|HASH}] (columns)
15616    ///     or: [FULLTEXT|SPATIAL] {INDEX|KEY} [USING {BTREE|HASH}] (columns)  -- no name
15617    fn parse_index_table_constraint(&mut self) -> Result<TableConstraint> {
15618        // Check for FULLTEXT or SPATIAL prefix
15619        let kind = if self.match_identifier("FULLTEXT") {
15620            Some("FULLTEXT".to_string())
15621        } else if self.match_identifier("SPATIAL") {
15622            Some("SPATIAL".to_string())
15623        } else {
15624            None
15625        };
15626
15627        // Consume INDEX or KEY keyword, track which was used
15628        let use_key_keyword = if self.match_token(TokenType::Key) {
15629            true
15630        } else {
15631            self.match_token(TokenType::Index);
15632            false
15633        };
15634
15635        // Check for USING before index name (MySQL allows: INDEX USING BTREE (col))
15636        let early_using = if self.check(TokenType::Using) {
15637            self.match_token(TokenType::Using);
15638            if self.match_identifier("BTREE") {
15639                Some("BTREE".to_string())
15640            } else if self.match_identifier("HASH") {
15641                Some("HASH".to_string())
15642            } else {
15643                None
15644            }
15645        } else {
15646            None
15647        };
15648
15649        // Optional index name (only if next token is not LParen or Using)
15650        let name = if !self.check(TokenType::LParen)
15651            && !self.check(TokenType::Using)
15652            && self.is_identifier_token()
15653        {
15654            Some(Identifier::new(self.advance().text))
15655        } else {
15656            None
15657        };
15658
15659        // Check for USING after index name (if not already parsed)
15660        let late_using = if early_using.is_none() && self.match_token(TokenType::Using) {
15661            if self.match_identifier("BTREE") {
15662                Some("BTREE".to_string())
15663            } else if self.match_identifier("HASH") {
15664                Some("HASH".to_string())
15665            } else {
15666                None
15667            }
15668        } else {
15669            None
15670        };
15671
15672        // Parse columns (with optional prefix length and DESC)
15673        self.expect(TokenType::LParen)?;
15674        let columns = self.parse_index_identifier_list()?;
15675        self.expect(TokenType::RParen)?;
15676
15677        // Parse optional constraint modifiers (USING after columns, COMMENT, etc.)
15678        let mut modifiers = self.parse_constraint_modifiers();
15679
15680        // Set the using value from wherever we found it
15681        // Both early_using (before name) and late_using (after name, before columns) mean USING is before columns
15682        if early_using.is_some() {
15683            modifiers.using = early_using;
15684            modifiers.using_before_columns = true;
15685        } else if late_using.is_some() {
15686            modifiers.using = late_using;
15687            modifiers.using_before_columns = true; // USING was after name but before columns
15688        }
15689        // If using was found in parse_constraint_modifiers (after columns), using_before_columns stays false
15690
15691        Ok(TableConstraint::Index {
15692            name,
15693            columns,
15694            kind,
15695            modifiers,
15696            use_key_keyword,
15697            expression: None,
15698            index_type: None,
15699            granularity: None,
15700        })
15701    }
15702
15703    /// Parse constraint modifiers like ENFORCED, DEFERRABLE, NORELY, USING, etc.
15704    fn parse_constraint_modifiers(&mut self) -> ConstraintModifiers {
15705        let mut modifiers = ConstraintModifiers::default();
15706        loop {
15707            if self.match_token(TokenType::Not) {
15708                // NOT ENFORCED, NOT DEFERRABLE, NOT VALID
15709                if self.match_identifier("ENFORCED") {
15710                    modifiers.enforced = Some(false);
15711                } else if self.match_identifier("DEFERRABLE") {
15712                    modifiers.deferrable = Some(false);
15713                } else if self.match_identifier("VALID") {
15714                    modifiers.not_valid = true;
15715                }
15716            } else if self.match_identifier("ENFORCED") {
15717                modifiers.enforced = Some(true);
15718            } else if self.match_identifier("DEFERRABLE") {
15719                modifiers.deferrable = Some(true);
15720            } else if self.match_identifier("INITIALLY") {
15721                // INITIALLY DEFERRED or INITIALLY IMMEDIATE
15722                if self.match_identifier("DEFERRED") {
15723                    modifiers.initially_deferred = Some(true);
15724                } else if self.match_identifier("IMMEDIATE") {
15725                    modifiers.initially_deferred = Some(false);
15726                }
15727            } else if self.match_identifier("NORELY") {
15728                modifiers.norely = true;
15729            } else if self.match_identifier("RELY") {
15730                modifiers.rely = true;
15731            } else if self.match_token(TokenType::Using) {
15732                // USING BTREE or USING HASH (MySQL)
15733                if self.match_identifier("BTREE") {
15734                    modifiers.using = Some("BTREE".to_string());
15735                } else if self.match_identifier("HASH") {
15736                    modifiers.using = Some("HASH".to_string());
15737                }
15738            } else if self.match_token(TokenType::Comment) {
15739                // MySQL index COMMENT 'text'
15740                if self.check(TokenType::String) {
15741                    modifiers.comment = Some(self.advance().text);
15742                }
15743            } else if self.match_identifier("VISIBLE") {
15744                modifiers.visible = Some(true);
15745            } else if self.match_identifier("INVISIBLE") {
15746                modifiers.visible = Some(false);
15747            } else if self.match_identifier("ENGINE_ATTRIBUTE") {
15748                // MySQL ENGINE_ATTRIBUTE = 'value'
15749                self.match_token(TokenType::Eq);
15750                if self.check(TokenType::String) {
15751                    modifiers.engine_attribute = Some(self.advance().text);
15752                }
15753            } else if self.check(TokenType::With) {
15754                let saved_with = self.current;
15755                self.skip(); // consume WITH
15756                if self.match_identifier("PARSER") {
15757                    // MySQL WITH PARSER name
15758                    if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
15759                        modifiers.with_parser = Some(self.advance().text);
15760                    }
15761                } else if self.check(TokenType::LParen) {
15762                    // TSQL: WITH (PAD_INDEX=ON, STATISTICS_NORECOMPUTE=OFF, ...)
15763                    // Parse and store the options
15764                    self.skip(); // consume (
15765                    loop {
15766                        if self.check(TokenType::RParen) || self.is_at_end() {
15767                            break;
15768                        }
15769                        // Parse KEY=VALUE pair
15770                        let key = self.advance().text.clone();
15771                        if self.match_token(TokenType::Eq) {
15772                            let value = self.advance().text.clone();
15773                            modifiers.with_options.push((key, value));
15774                        }
15775                        if !self.match_token(TokenType::Comma) {
15776                            break;
15777                        }
15778                    }
15779                    let _ = self.match_token(TokenType::RParen);
15780                } else {
15781                    // Not WITH PARSER or WITH (...), backtrack
15782                    self.current = saved_with;
15783                    break;
15784                }
15785            } else if self.check(TokenType::On) {
15786                let saved_on = self.current;
15787                self.skip(); // consume ON
15788                if self.match_identifier("CONFLICT") {
15789                    // SQLite ON CONFLICT action: ROLLBACK, ABORT, FAIL, IGNORE, REPLACE
15790                    if self.match_token(TokenType::Rollback) {
15791                        modifiers.on_conflict = Some("ROLLBACK".to_string());
15792                    } else if self.match_identifier("ABORT") {
15793                        modifiers.on_conflict = Some("ABORT".to_string());
15794                    } else if self.match_identifier("FAIL") {
15795                        modifiers.on_conflict = Some("FAIL".to_string());
15796                    } else if self.match_token(TokenType::Ignore) {
15797                        modifiers.on_conflict = Some("IGNORE".to_string());
15798                    } else if self.match_token(TokenType::Replace) {
15799                        modifiers.on_conflict = Some("REPLACE".to_string());
15800                    }
15801                } else if self.is_identifier_token() || self.check(TokenType::QuotedIdentifier) {
15802                    // TSQL: ON [filegroup] - parse and store
15803                    let quoted = self.check(TokenType::QuotedIdentifier);
15804                    let name = self.advance().text.clone();
15805                    modifiers.on_filegroup = Some(Identifier {
15806                        name,
15807                        quoted,
15808                        trailing_comments: Vec::new(),
15809                        span: None,
15810                    });
15811                } else {
15812                    // Unknown ON clause, backtrack
15813                    self.current = saved_on;
15814                    break;
15815                }
15816            } else {
15817                break;
15818            }
15819        }
15820        modifiers
15821    }
15822
15823    /// Parse foreign key reference
15824    fn parse_foreign_key_ref(&mut self) -> Result<ForeignKeyRef> {
15825        let table = self.parse_table_ref()?;
15826
15827        let columns = if self.match_token(TokenType::LParen) {
15828            let cols = self.parse_identifier_list()?;
15829            self.expect(TokenType::RParen)?;
15830            cols
15831        } else {
15832            Vec::new()
15833        };
15834
15835        // Handle optional MATCH clause (MATCH FULL, MATCH PARTIAL, MATCH SIMPLE)
15836        // MATCH clause comes BEFORE ON DELETE/ON UPDATE in PostgreSQL
15837        let match_type = if self.match_token(TokenType::Match) {
15838            if self.check(TokenType::Full) {
15839                self.skip();
15840                Some(MatchType::Full)
15841            } else if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
15842                let text = self.advance().text.to_ascii_uppercase();
15843                match text.as_str() {
15844                    "PARTIAL" => Some(MatchType::Partial),
15845                    "SIMPLE" => Some(MatchType::Simple),
15846                    _ => None,
15847                }
15848            } else {
15849                None
15850            }
15851        } else {
15852            None
15853        };
15854
15855        // ON DELETE and ON UPDATE can appear in either order
15856        let mut on_delete = None;
15857        let mut on_update = None;
15858        let mut on_update_first = false;
15859        let mut first_clause = true;
15860
15861        // Try parsing up to 2 ON clauses
15862        for _ in 0..2 {
15863            if on_delete.is_none() && self.match_keywords(&[TokenType::On, TokenType::Delete]) {
15864                on_delete = Some(self.parse_referential_action()?);
15865            } else if on_update.is_none()
15866                && self.match_keywords(&[TokenType::On, TokenType::Update])
15867            {
15868                if first_clause {
15869                    on_update_first = true;
15870                }
15871                on_update = Some(self.parse_referential_action()?);
15872            } else {
15873                break;
15874            }
15875            first_clause = false;
15876        }
15877
15878        // MATCH clause can also appear after ON DELETE/ON UPDATE
15879        let mut match_after_actions = false;
15880        let match_type = if match_type.is_none() && self.match_token(TokenType::Match) {
15881            match_after_actions = on_delete.is_some() || on_update.is_some();
15882            if self.check(TokenType::Full) {
15883                self.skip();
15884                Some(MatchType::Full)
15885            } else if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
15886                let text = self.advance().text.to_ascii_uppercase();
15887                match text.as_str() {
15888                    "PARTIAL" => Some(MatchType::Partial),
15889                    "SIMPLE" => Some(MatchType::Simple),
15890                    _ => None,
15891                }
15892            } else {
15893                None
15894            }
15895        } else {
15896            match_type
15897        };
15898
15899        // Handle optional DEFERRABLE / NOT DEFERRABLE
15900        let deferrable = if self.match_identifier("DEFERRABLE") {
15901            Some(true)
15902        } else if self.match_token(TokenType::Not) && self.match_identifier("DEFERRABLE") {
15903            Some(false)
15904        } else {
15905            None
15906        };
15907
15908        Ok(ForeignKeyRef {
15909            table,
15910            columns,
15911            on_delete,
15912            on_update,
15913            on_update_first,
15914            match_type,
15915            match_after_actions,
15916            constraint_name: None, // Will be set by caller if CONSTRAINT was used
15917            deferrable,
15918            has_foreign_key_keywords: false, // Will be set by caller if FOREIGN KEY preceded REFERENCES
15919        })
15920    }
15921
15922    /// Parse referential action (CASCADE, SET NULL, etc.)
15923    fn parse_referential_action(&mut self) -> Result<ReferentialAction> {
15924        if self.match_token(TokenType::Cascade) {
15925            Ok(ReferentialAction::Cascade)
15926        } else if self.match_keywords(&[TokenType::Set, TokenType::Null]) {
15927            Ok(ReferentialAction::SetNull)
15928        } else if self.match_keywords(&[TokenType::Set, TokenType::Default]) {
15929            Ok(ReferentialAction::SetDefault)
15930        } else if self.match_token(TokenType::Restrict) {
15931            Ok(ReferentialAction::Restrict)
15932        } else if self.match_token(TokenType::No) {
15933            // NO ACTION - NO is a token, ACTION is an identifier
15934            if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("ACTION") {
15935                self.skip();
15936            }
15937            Ok(ReferentialAction::NoAction)
15938        } else {
15939            Err(self.parse_error("Expected CASCADE, SET NULL, SET DEFAULT, RESTRICT, or NO ACTION"))
15940        }
15941    }
15942
15943    /// Parse Snowflake TAG clause: TAG (key='value', key2='value2')
15944    fn parse_tags(&mut self) -> Result<Tags> {
15945        self.expect(TokenType::LParen)?;
15946        let mut expressions = Vec::new();
15947
15948        loop {
15949            // Parse key = 'value' as a Property expression
15950            let key = self.expect_identifier_or_keyword()?;
15951            self.expect(TokenType::Eq)?;
15952            let value = self.parse_primary()?;
15953
15954            // Create a Property expression: key = value
15955            expressions.push(Expression::Property(Box::new(Property {
15956                this: Box::new(Expression::Identifier(Identifier::new(key))),
15957                value: Some(Box::new(value)),
15958            })));
15959
15960            if !self.match_token(TokenType::Comma) {
15961                break;
15962            }
15963        }
15964
15965        self.expect(TokenType::RParen)?;
15966
15967        Ok(Tags { expressions })
15968    }
15969
15970    /// Parse CREATE VIEW
15971    fn parse_create_view(
15972        &mut self,
15973        or_replace: bool,
15974        materialized: bool,
15975        temporary: bool,
15976        algorithm: Option<String>,
15977        definer: Option<String>,
15978        security: Option<FunctionSecurity>,
15979        secure: bool,
15980    ) -> Result<Expression> {
15981        self.expect(TokenType::View)?;
15982
15983        // Handle IF NOT EXISTS
15984        let if_not_exists =
15985            self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists]);
15986
15987        let name = self.parse_table_ref()?;
15988
15989        // ClickHouse: UUID 'xxx' clause after view name
15990        if matches!(
15991            self.config.dialect,
15992            Some(crate::dialects::DialectType::ClickHouse)
15993        ) && self.check_identifier("UUID")
15994        {
15995            self.skip(); // consume UUID
15996            let _ = self.advance(); // consume UUID string value
15997        }
15998
15999        // ClickHouse: ON CLUSTER clause (after view name)
16000        let on_cluster = self.parse_on_cluster_clause()?;
16001
16002        // ClickHouse: TO destination_table clause
16003        let to_table = if self.match_token(TokenType::To) {
16004            Some(self.parse_table_ref()?)
16005        } else {
16006            None
16007        };
16008
16009        // Snowflake: COPY GRANTS (before column list)
16010        let copy_grants = self.match_text_seq(&["COPY", "GRANTS"]);
16011
16012        // For materialized views, column definitions can include data types: (c1 INT, c2 INT)
16013        // This applies to Doris, ClickHouse, and potentially other dialects
16014        // We need to parse this as a schema instead of simple column names
16015        // Track if we parsed a schema (with types) vs simple columns
16016        let mut schema: Option<Schema> = None;
16017        let mut unique_key: Option<UniqueKeyProperty> = None;
16018
16019        // Optional column list with optional COMMENT and OPTIONS per column
16020        let columns = if self.check(TokenType::LParen) {
16021            // For materialized views or ClickHouse views, try to parse as schema with typed columns
16022            if materialized
16023                || matches!(
16024                    self.config.dialect,
16025                    Some(crate::dialects::DialectType::ClickHouse)
16026                )
16027            {
16028                // Save position to backtrack if needed
16029                let saved_pos = self.current;
16030
16031                // Try to parse as schema (with typed columns)
16032                if let Some(Expression::Schema(parsed_schema)) = self.parse_schema()? {
16033                    schema = Some(*parsed_schema);
16034
16035                    // Doris: KEY (columns) after schema
16036                    if self.match_text_seq(&["KEY"]) {
16037                        let exprs = self.parse_composite_key_expressions()?;
16038                        unique_key = Some(UniqueKeyProperty { expressions: exprs });
16039                    }
16040
16041                    Vec::new() // Use schema instead of columns
16042                } else {
16043                    // Backtrack and parse as simple columns
16044                    self.current = saved_pos;
16045                    self.parse_view_columns()?
16046                }
16047            } else {
16048                self.parse_view_columns()?
16049            }
16050        } else {
16051            Vec::new()
16052        };
16053
16054        // Snowflake: COPY GRANTS can also appear after column list
16055        let copy_grants = copy_grants || self.match_text_seq(&["COPY", "GRANTS"]);
16056
16057        // Presto/Trino/StarRocks: SECURITY DEFINER/INVOKER/NONE (after view name, before AS)
16058        // This differs from MySQL's SQL SECURITY which comes before VIEW keyword
16059        let (security, security_sql_style) = if security.is_some() {
16060            // MySQL-style SQL SECURITY was parsed before VIEW keyword
16061            (security, true)
16062        } else if self.match_identifier("SECURITY") {
16063            // Presto-style SECURITY after view name
16064            let sec = if self.match_identifier("DEFINER") {
16065                Some(FunctionSecurity::Definer)
16066            } else if self.match_identifier("INVOKER") {
16067                Some(FunctionSecurity::Invoker)
16068            } else if self.match_identifier("NONE") {
16069                Some(FunctionSecurity::None)
16070            } else {
16071                None
16072            };
16073            (sec, false)
16074        } else {
16075            (None, true)
16076        };
16077
16078        // Snowflake: COMMENT = 'text'
16079        let view_comment = if self.match_token(TokenType::Comment) {
16080            // Match = or skip if not present (some dialects use COMMENT='text')
16081            let _ = self.match_token(TokenType::Eq);
16082            Some(self.expect_string()?)
16083        } else {
16084            None
16085        };
16086
16087        // Snowflake: TAG (name='value', ...)
16088        let tags = if self.match_identifier("TAG") {
16089            let mut tag_list = Vec::new();
16090            if self.match_token(TokenType::LParen) {
16091                loop {
16092                    let tag_name = self.expect_identifier()?;
16093                    let tag_value = if self.match_token(TokenType::Eq) {
16094                        self.expect_string()?
16095                    } else {
16096                        String::new()
16097                    };
16098                    tag_list.push((tag_name, tag_value));
16099                    if !self.match_token(TokenType::Comma) {
16100                        break;
16101                    }
16102                }
16103                self.expect(TokenType::RParen)?;
16104            }
16105            tag_list
16106        } else {
16107            Vec::new()
16108        };
16109
16110        // BigQuery: OPTIONS (key=value, ...)
16111        let options = if self.match_identifier("OPTIONS") {
16112            self.parse_options_list()?
16113        } else {
16114            Vec::new()
16115        };
16116
16117        // Doris: BUILD IMMEDIATE/DEFERRED for materialized views
16118        let build = if self.match_identifier("BUILD") {
16119            if self.match_identifier("IMMEDIATE") {
16120                Some("IMMEDIATE".to_string())
16121            } else if self.match_identifier("DEFERRED") {
16122                Some("DEFERRED".to_string())
16123            } else {
16124                // Unexpected token after BUILD - try to consume it
16125                let value = self.expect_identifier_or_keyword()?;
16126                Some(value.to_ascii_uppercase())
16127            }
16128        } else {
16129            None
16130        };
16131
16132        // Doris: REFRESH COMPLETE/AUTO ON MANUAL/COMMIT/SCHEDULE [EVERY n UNIT] [STARTS 'datetime']
16133        // ClickHouse: REFRESH AFTER interval / REFRESH EVERY interval [OFFSET interval] [RANDOMIZE FOR interval] [APPEND]
16134        let refresh = if self.match_token(TokenType::Refresh) {
16135            if matches!(
16136                self.config.dialect,
16137                Some(crate::dialects::DialectType::ClickHouse)
16138            ) {
16139                // ClickHouse REFRESH syntax: consume tokens until AS/POPULATE/TO/ENGINE or end
16140                while !self.is_at_end()
16141                    && !self.check(TokenType::As)
16142                    && !self.check_identifier("POPULATE")
16143                    && !self.check_identifier("TO")
16144                    && !self.check_identifier("APPEND")
16145                    && !self.check_identifier("ENGINE")
16146                    && !self.check(TokenType::Semicolon)
16147                {
16148                    self.skip();
16149                }
16150                // Consume APPEND if present (REFRESH ... APPEND TO target)
16151                let _ = self.match_identifier("APPEND");
16152                None
16153            } else {
16154                Some(Box::new(self.parse_refresh_trigger_property()?))
16155            }
16156        } else {
16157            None
16158        };
16159
16160        // ClickHouse: TO destination_table after REFRESH ... APPEND
16161        // e.g., CREATE MATERIALIZED VIEW v REFRESH AFTER 1 SECOND APPEND TO tab (cols) EMPTY AS ...
16162        let to_table = if to_table.is_none() && self.match_token(TokenType::To) {
16163            Some(self.parse_table_ref()?)
16164        } else {
16165            to_table
16166        };
16167
16168        // ClickHouse: column definitions after REFRESH ... APPEND TO tab (cols)
16169        if schema.is_none()
16170            && self.check(TokenType::LParen)
16171            && matches!(
16172                self.config.dialect,
16173                Some(crate::dialects::DialectType::ClickHouse)
16174            )
16175        {
16176            let saved_pos = self.current;
16177            if let Some(Expression::Schema(parsed_schema)) = self.parse_schema()? {
16178                schema = Some(*parsed_schema);
16179            } else {
16180                self.current = saved_pos;
16181            }
16182        }
16183
16184        // Redshift: AUTO REFRESH YES|NO for materialized views
16185        let auto_refresh = if self.match_text_seq(&["AUTO", "REFRESH"]) {
16186            if self.match_identifier("YES") {
16187                Some(true)
16188            } else if self.match_identifier("NO") {
16189                Some(false)
16190            } else {
16191                None
16192            }
16193        } else {
16194            None
16195        };
16196
16197        // ClickHouse: Parse table properties (ENGINE, ORDER BY, SAMPLE, SETTINGS, TTL, etc.)
16198        // These appear after column definitions but before AS clause for materialized views
16199        let mut table_properties = Vec::new();
16200        if materialized
16201            && matches!(
16202                self.config.dialect,
16203                Some(crate::dialects::DialectType::ClickHouse)
16204            )
16205        {
16206            self.parse_clickhouse_table_properties(&mut table_properties)?;
16207        }
16208
16209        // ClickHouse: POPULATE / EMPTY keywords before AS in materialized views
16210        if materialized
16211            && matches!(
16212                self.config.dialect,
16213                Some(crate::dialects::DialectType::ClickHouse)
16214            )
16215        {
16216            let _ = self.match_identifier("POPULATE");
16217            let _ = self.match_identifier("EMPTY");
16218        }
16219
16220        // AS is optional - some dialects (e.g., Presto) allow SELECT without AS
16221        let has_as = self.match_token(TokenType::As);
16222        if !has_as && !self.check(TokenType::Select) && !self.check(TokenType::With) {
16223            // No AS and no SELECT/WITH means no query - return empty view (for partial statements)
16224            return Ok(Expression::CreateView(Box::new(CreateView {
16225                name,
16226                columns,
16227                query: Expression::Null(Null), // Placeholder for incomplete VIEW
16228                or_replace,
16229                if_not_exists,
16230                materialized,
16231                temporary,
16232                secure,
16233                algorithm,
16234                definer,
16235                security,
16236                security_sql_style,
16237                query_parenthesized: false,
16238                locking_mode: None,
16239                locking_access: None,
16240                copy_grants,
16241                comment: view_comment,
16242                tags,
16243                options,
16244                build,
16245                refresh,
16246                schema: schema.map(Box::new),
16247                unique_key: unique_key.map(Box::new),
16248                no_schema_binding: false,
16249                auto_refresh,
16250                on_cluster,
16251                to_table,
16252                table_properties,
16253            })));
16254        }
16255
16256        // Parse Teradata LOCKING clause: LOCKING ROW|TABLE|DATABASE FOR ACCESS|READ|WRITE
16257        let mut locking_mode: Option<String> = None;
16258        let mut locking_access: Option<String> = None;
16259        if self.match_token(TokenType::Lock) || self.match_identifier("LOCKING") {
16260            // Capture: ROW, TABLE, DATABASE, etc.
16261            if self.match_token(TokenType::Row) {
16262                locking_mode = Some("ROW".to_string());
16263            } else if self.match_token(TokenType::Table) {
16264                locking_mode = Some("TABLE".to_string());
16265            } else if self.match_token(TokenType::Database) || self.match_identifier("DATABASE") {
16266                locking_mode = Some("DATABASE".to_string());
16267            }
16268            // Capture FOR ACCESS|READ|WRITE
16269            if self.match_token(TokenType::For) {
16270                if self.match_identifier("ACCESS") {
16271                    locking_access = Some("ACCESS".to_string());
16272                } else if self.match_identifier("READ") {
16273                    locking_access = Some("READ".to_string());
16274                } else if self.match_identifier("WRITE") {
16275                    locking_access = Some("WRITE".to_string());
16276                }
16277            }
16278        }
16279
16280        // Use parse_statement to handle SELECT, WITH...SELECT, or (SELECT...)
16281        let query_parenthesized = self.check(TokenType::LParen);
16282        let query = if self.check(TokenType::With) {
16283            self.parse_statement()?
16284        } else if query_parenthesized {
16285            // Handle (SELECT ...) or (WITH ... SELECT ...) - parenthesized query
16286            self.skip(); // consume (
16287            let inner = if self.check(TokenType::With) {
16288                self.parse_statement()?
16289            } else {
16290                self.parse_select()?
16291            };
16292            self.expect(TokenType::RParen)?;
16293            inner
16294        } else {
16295            self.parse_select()?
16296        };
16297
16298        // Redshift: WITH NO SCHEMA BINDING (after the query)
16299        let no_schema_binding = self.match_text_seq(&["WITH", "NO", "SCHEMA", "BINDING"]);
16300
16301        Ok(Expression::CreateView(Box::new(CreateView {
16302            name,
16303            columns,
16304            query,
16305            or_replace,
16306            if_not_exists,
16307            materialized,
16308            temporary,
16309            secure,
16310            algorithm,
16311            definer,
16312            security,
16313            security_sql_style,
16314            query_parenthesized,
16315            locking_mode,
16316            locking_access,
16317            copy_grants,
16318            comment: view_comment,
16319            tags,
16320            options,
16321            build,
16322            refresh,
16323            schema: schema.map(Box::new),
16324            unique_key: unique_key.map(Box::new),
16325            no_schema_binding,
16326            auto_refresh,
16327            on_cluster,
16328            to_table,
16329            table_properties,
16330        })))
16331    }
16332
16333    /// Parse view column list: (col1, col2 OPTIONS(...) COMMENT 'text', ...)
16334    /// For simple view definitions without data types
16335    fn parse_view_columns(&mut self) -> Result<Vec<ViewColumn>> {
16336        self.expect(TokenType::LParen)?;
16337        let mut cols = Vec::new();
16338        loop {
16339            let col_name = self.expect_identifier()?;
16340            // BigQuery: OPTIONS (key=value, ...) on view column
16341            let options = if self.match_identifier("OPTIONS") {
16342                self.parse_options_list()?
16343            } else {
16344                Vec::new()
16345            };
16346            // Optional COMMENT 'text'
16347            let comment = if self.match_token(TokenType::Comment) {
16348                Some(self.expect_string()?)
16349            } else {
16350                None
16351            };
16352            cols.push(ViewColumn {
16353                name: Identifier::new(col_name),
16354                comment,
16355                options,
16356            });
16357            if !self.match_token(TokenType::Comma) {
16358                break;
16359            }
16360        }
16361        self.expect(TokenType::RParen)?;
16362        Ok(cols)
16363    }
16364
16365    /// Parse CREATE [CLUSTERED|NONCLUSTERED] INDEX
16366    fn parse_create_index_with_clustered(
16367        &mut self,
16368        unique: bool,
16369        clustered: Option<String>,
16370    ) -> Result<Expression> {
16371        self.expect(TokenType::Index)?;
16372
16373        // PostgreSQL: CREATE INDEX CONCURRENTLY idx ON t(c)
16374        let concurrently = self.match_identifier("CONCURRENTLY");
16375
16376        // Handle IF NOT EXISTS
16377        let if_not_exists =
16378            self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists]);
16379
16380        // Index name is optional when IF NOT EXISTS is specified (PostgreSQL)
16381        let name = if if_not_exists && self.check(TokenType::On) {
16382            Identifier::new("") // Empty name when omitted
16383        } else {
16384            self.expect_identifier_with_quoted()?
16385        };
16386        self.expect(TokenType::On)?;
16387        let table = self.parse_table_ref()?;
16388
16389        // Optional USING clause
16390        let using = if self.match_token(TokenType::Using) {
16391            Some(self.expect_identifier()?)
16392        } else {
16393            None
16394        };
16395
16396        // Parse index columns (optional for COLUMNSTORE indexes)
16397        let columns = if self.match_token(TokenType::LParen) {
16398            let cols = self.parse_index_columns()?;
16399            self.expect(TokenType::RParen)?;
16400            cols
16401        } else if clustered
16402            .as_ref()
16403            .is_some_and(|c| c.contains("COLUMNSTORE"))
16404        {
16405            // COLUMNSTORE indexes don't require a column list
16406            Vec::new()
16407        } else if matches!(
16408            self.config.dialect,
16409            Some(crate::dialects::DialectType::ClickHouse)
16410        ) {
16411            // ClickHouse: CREATE INDEX idx ON table expr TYPE minmax GRANULARITY 1
16412            // No parentheses around the expression — consume to semicolon as Command
16413            let mut parts = vec![
16414                "CREATE".to_string(),
16415                if unique {
16416                    "UNIQUE INDEX".to_string()
16417                } else {
16418                    "INDEX".to_string()
16419                },
16420                name.name.clone(),
16421                "ON".to_string(),
16422            ];
16423            // Rebuild table name
16424            if let Some(ref s) = table.schema {
16425                parts.push(format!("{}.{}", s.name, table.name.name));
16426            } else {
16427                parts.push(table.name.name.clone());
16428            }
16429            while !self.is_at_end() && !self.check(TokenType::Semicolon) {
16430                let token = self.advance();
16431                if token.token_type == TokenType::String {
16432                    parts.push(format!("'{}'", token.text));
16433                } else if token.token_type == TokenType::QuotedIdentifier {
16434                    parts.push(format!("\"{}\"", token.text));
16435                } else {
16436                    parts.push(token.text.clone());
16437                }
16438            }
16439            return Ok(Expression::Command(Box::new(crate::expressions::Command {
16440                this: parts.join(" "),
16441            })));
16442        } else {
16443            self.expect(TokenType::LParen)?;
16444            let cols = self.parse_index_columns()?;
16445            self.expect(TokenType::RParen)?;
16446            cols
16447        };
16448
16449        // PostgreSQL: INCLUDE (col1, col2) clause
16450        let include_columns = if self.match_identifier("INCLUDE") {
16451            self.expect(TokenType::LParen)?;
16452            let mut cols = Vec::new();
16453            loop {
16454                cols.push(self.expect_identifier_with_quoted()?);
16455                if !self.match_token(TokenType::Comma) {
16456                    break;
16457                }
16458            }
16459            self.expect(TokenType::RParen)?;
16460            cols
16461        } else {
16462            Vec::new()
16463        };
16464
16465        // TSQL: WITH (option=value, ...) clause for index options
16466        let with_options = if self.check(TokenType::With) {
16467            // parse_with_properties expects the WITH keyword to NOT be consumed
16468            // but we need to check if we have WITH followed by LParen
16469            if self
16470                .peek_nth(1)
16471                .is_some_and(|t| t.token_type == TokenType::LParen)
16472            {
16473                self.skip(); // consume WITH
16474                self.parse_with_properties()?
16475            } else {
16476                Vec::new()
16477            }
16478        } else {
16479            Vec::new()
16480        };
16481
16482        // PostgreSQL: WHERE clause for partial indexes
16483        let where_clause = if self.match_token(TokenType::Where) {
16484            Some(Box::new(self.parse_expression()?))
16485        } else {
16486            None
16487        };
16488
16489        // TSQL: ON filegroup or partition scheme clause
16490        // e.g., ON PRIMARY, ON X([y])
16491        let on_filegroup = if self.match_token(TokenType::On) {
16492            // Get the filegroup/partition scheme name
16493            let token = self.advance();
16494            let mut filegroup = token.text.clone();
16495            // Check for partition scheme with column: ON partition_scheme(column)
16496            if self.match_token(TokenType::LParen) {
16497                filegroup.push('(');
16498                // Parse the partition column(s)
16499                loop {
16500                    let col_token = self.advance();
16501                    // For TSQL, use bracket quoting for quoted identifiers
16502                    if col_token.token_type == TokenType::QuotedIdentifier {
16503                        filegroup.push('[');
16504                        filegroup.push_str(&col_token.text);
16505                        filegroup.push(']');
16506                    } else {
16507                        filegroup.push_str(&col_token.text);
16508                    }
16509                    if !self.match_token(TokenType::Comma) {
16510                        break;
16511                    }
16512                    filegroup.push_str(", ");
16513                }
16514                self.expect(TokenType::RParen)?;
16515                filegroup.push(')');
16516            }
16517            Some(filegroup)
16518        } else {
16519            None
16520        };
16521
16522        Ok(Expression::CreateIndex(Box::new(CreateIndex {
16523            name,
16524            table,
16525            columns,
16526            unique,
16527            if_not_exists,
16528            using,
16529            clustered,
16530            concurrently,
16531            where_clause,
16532            include_columns,
16533            with_options,
16534            on_filegroup,
16535        })))
16536    }
16537
16538    /// Parse index columns - can be identifiers or expressions (like function calls)
16539    fn parse_index_columns(&mut self) -> Result<Vec<IndexColumn>> {
16540        let mut columns = Vec::new();
16541        loop {
16542            // Parse as expression to handle function calls like BOX(location, location)
16543            let expr = self.parse_expression()?;
16544
16545            // Extract column name from expression
16546            let column = match &expr {
16547                Expression::Identifier(ident) => ident.clone(),
16548                Expression::Column(col) => {
16549                    // For column expressions (e.g., simple identifier like [Col]),
16550                    // extract the identifier directly to preserve quoting
16551                    col.name.clone()
16552                }
16553                Expression::Function(_func) => {
16554                    // For function expressions, create an identifier from the function call
16555                    Identifier::new(self.expression_to_sql(&expr))
16556                }
16557                _ => Identifier::new(self.expression_to_sql(&expr)),
16558            };
16559
16560            // Parse optional PostgreSQL operator class (e.g., varchar_pattern_ops, public.gin_trgm_ops)
16561            // An opclass is an identifier that appears before ASC/DESC/NULLS and is not a keyword
16562            let opclass = if self.is_identifier_token()
16563                && !self.check(TokenType::Asc)
16564                && !self.check(TokenType::Desc)
16565                && !self.check(TokenType::Nulls)
16566            {
16567                let mut opclass_name = self.advance().text;
16568                // Handle qualified opclass names like public.gin_trgm_ops
16569                while self.match_token(TokenType::Dot) {
16570                    opclass_name.push('.');
16571                    if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
16572                        opclass_name.push_str(&self.advance().text);
16573                    }
16574                }
16575                Some(opclass_name)
16576            } else {
16577                None
16578            };
16579
16580            let desc = self.match_token(TokenType::Desc);
16581            let asc = if !desc {
16582                self.match_token(TokenType::Asc)
16583            } else {
16584                false
16585            };
16586            let nulls_first = if self.match_token(TokenType::Nulls) {
16587                if self.match_token(TokenType::First) {
16588                    Some(true)
16589                } else if self.match_token(TokenType::Last) {
16590                    Some(false)
16591                } else {
16592                    None
16593                }
16594            } else {
16595                None
16596            };
16597            columns.push(IndexColumn {
16598                column,
16599                desc,
16600                asc,
16601                nulls_first,
16602                opclass,
16603            });
16604            if !self.match_token(TokenType::Comma) {
16605                break;
16606            }
16607        }
16608        Ok(columns)
16609    }
16610
16611    /// Convert an expression to its SQL string representation (simple version for index expressions)
16612    fn expression_to_sql(&self, expr: &Expression) -> String {
16613        match expr {
16614            Expression::Identifier(ident) => ident.name.clone(),
16615            Expression::Function(func) => {
16616                let args = func
16617                    .args
16618                    .iter()
16619                    .map(|a| self.expression_to_sql(a))
16620                    .collect::<Vec<_>>()
16621                    .join(", ");
16622                format!("{}({})", func.name, args)
16623            }
16624            Expression::Column(col) => {
16625                if let Some(ref table) = col.table {
16626                    format!("{}.{}", table, col.name)
16627                } else {
16628                    col.name.to_string()
16629                }
16630            }
16631            Expression::Literal(lit) => match lit {
16632                Literal::String(s) => format!("'{}'", s),
16633                Literal::Number(n) => n.clone(),
16634                _ => "?".to_string(),
16635            },
16636            Expression::Null(_) => "NULL".to_string(),
16637            Expression::Boolean(b) => {
16638                if b.value {
16639                    "TRUE".to_string()
16640                } else {
16641                    "FALSE".to_string()
16642                }
16643            }
16644            _ => "?".to_string(),
16645        }
16646    }
16647
16648    /// Parse DROP statement
16649    fn parse_drop(&mut self) -> Result<Expression> {
16650        // Capture leading comments from the DROP token (e.g., "-- comment\nDROP TABLE ...")
16651        let leading_comments = self.current_leading_comments().to_vec();
16652        self.expect(TokenType::Drop)?;
16653
16654        // ClickHouse: DROP TEMPORARY TABLE / DROP TEMPORARY VIEW
16655        if self.check(TokenType::Temporary)
16656            && matches!(
16657                self.config.dialect,
16658                Some(crate::dialects::DialectType::ClickHouse)
16659            )
16660        {
16661            self.skip(); // consume TEMPORARY
16662            if self.check(TokenType::View) {
16663                return self.parse_drop_view(false);
16664            }
16665            return self.parse_drop_table(leading_comments.clone());
16666        }
16667
16668        match self.peek().token_type {
16669            TokenType::Table => self.parse_drop_table(leading_comments),
16670            TokenType::View => self.parse_drop_view(false),
16671            TokenType::Materialized => {
16672                self.skip(); // consume MATERIALIZED
16673                self.parse_drop_view(true)
16674            }
16675            TokenType::Index => self.parse_drop_index(),
16676            TokenType::Schema => self.parse_drop_schema(),
16677            TokenType::Database => self.parse_drop_database(),
16678            TokenType::Function => self.parse_drop_function(),
16679            TokenType::Procedure => self.parse_drop_procedure(),
16680            TokenType::Sequence => self.parse_drop_sequence(),
16681            TokenType::Trigger => self.parse_drop_trigger(),
16682            TokenType::Type => self.parse_drop_type(),
16683            TokenType::Domain => {
16684                // DROP DOMAIN is similar to DROP TYPE
16685                self.skip();
16686                let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
16687                let name = self.parse_table_ref()?;
16688                let cascade = self.match_token(TokenType::Cascade);
16689                if !cascade {
16690                    self.match_token(TokenType::Restrict);
16691                }
16692                Ok(Expression::DropType(Box::new(DropType {
16693                    name,
16694                    if_exists,
16695                    cascade,
16696                })))
16697            }
16698            TokenType::Namespace => {
16699                // DROP NAMESPACE is similar to DROP SCHEMA (Spark/Databricks)
16700                self.skip();
16701                let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
16702                // Parse potentially qualified namespace name (a.b.c)
16703                let mut name_parts = vec![self.expect_identifier()?];
16704                while self.match_token(TokenType::Dot) {
16705                    name_parts.push(self.expect_identifier()?);
16706                }
16707                let name = Identifier::new(name_parts.join("."));
16708                let cascade = self.match_token(TokenType::Cascade);
16709                if !cascade {
16710                    self.match_token(TokenType::Restrict);
16711                }
16712                Ok(Expression::DropNamespace(Box::new(DropNamespace {
16713                    name,
16714                    if_exists,
16715                    cascade,
16716                })))
16717            }
16718            _ => {
16719                // ClickHouse: DROP DICTIONARY, DROP USER, DROP QUOTA, DROP ROLE,
16720                // DROP ROW POLICY, DROP SETTINGS PROFILE, DROP NAMED COLLECTION
16721                if matches!(
16722                    self.config.dialect,
16723                    Some(crate::dialects::DialectType::ClickHouse)
16724                ) {
16725                    let text_upper = self.peek().text.to_ascii_uppercase();
16726                    if matches!(
16727                        text_upper.as_str(),
16728                        "DICTIONARY"
16729                            | "USER"
16730                            | "QUOTA"
16731                            | "ROLE"
16732                            | "ROW"
16733                            | "POLICY"
16734                            | "NAMED"
16735                            | "WORKLOAD"
16736                            | "RESOURCE"
16737                            | "PROFILE"
16738                    ) || self.check(TokenType::Settings)
16739                        || self.check(TokenType::Partition)
16740                    {
16741                        self.skip(); // consume keyword, previous() is now set
16742                        let mut tokens: Vec<(String, TokenType)> = vec![
16743                            ("DROP".to_string(), TokenType::Var),
16744                            (
16745                                self.previous().text.to_ascii_uppercase(),
16746                                self.previous().token_type,
16747                            ),
16748                        ];
16749                        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
16750                            let token = self.advance();
16751                            let text = if token.token_type == TokenType::QuotedIdentifier {
16752                                format!("\"{}\"", token.text)
16753                            } else if token.token_type == TokenType::String {
16754                                format!("'{}'", token.text)
16755                            } else {
16756                                token.text.clone()
16757                            };
16758                            tokens.push((text, token.token_type));
16759                        }
16760                        return Ok(Expression::Command(Box::new(Command {
16761                            this: self.join_command_tokens(tokens),
16762                        })));
16763                    }
16764                }
16765                Err(self.parse_error(format!(
16766                    "Expected TABLE, VIEW, INDEX, SCHEMA, DATABASE, FUNCTION, PROCEDURE, SEQUENCE, TRIGGER, TYPE, or NAMESPACE after DROP, got {:?}",
16767                    self.peek().token_type
16768                )))
16769            }
16770        }
16771    }
16772
16773    /// Parse DROP TABLE
16774    fn parse_drop_table(&mut self, leading_comments: Vec<String>) -> Result<Expression> {
16775        self.expect(TokenType::Table)?;
16776
16777        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
16778
16779        // ClickHouse: IF EMPTY
16780        if !if_exists
16781            && matches!(
16782                self.config.dialect,
16783                Some(crate::dialects::DialectType::ClickHouse)
16784            )
16785        {
16786            if self.check(TokenType::If)
16787                && self.current + 1 < self.tokens.len()
16788                && self.tokens[self.current + 1]
16789                    .text
16790                    .eq_ignore_ascii_case("EMPTY")
16791            {
16792                self.skip(); // consume IF
16793                self.skip(); // consume EMPTY
16794            }
16795        }
16796
16797        // Parse table names (can be multiple)
16798        let mut names = Vec::new();
16799        loop {
16800            names.push(self.parse_table_ref()?);
16801            if !self.match_token(TokenType::Comma) {
16802                break;
16803            }
16804        }
16805
16806        // Handle CASCADE [CONSTRAINTS] or RESTRICT
16807        let mut cascade = false;
16808        let mut cascade_constraints = false;
16809        if self.match_token(TokenType::Cascade) {
16810            if self.match_identifier("CONSTRAINTS") {
16811                cascade_constraints = true;
16812            } else {
16813                cascade = true;
16814            }
16815        } else {
16816            self.match_token(TokenType::Restrict); // consume optional RESTRICT
16817        }
16818
16819        // Handle PURGE (Oracle)
16820        let purge = self.match_identifier("PURGE");
16821
16822        // ClickHouse: ON CLUSTER clause
16823        if matches!(
16824            self.config.dialect,
16825            Some(crate::dialects::DialectType::ClickHouse)
16826        ) {
16827            let _ = self.parse_on_cluster_clause()?;
16828        }
16829
16830        // ClickHouse: SYNC keyword
16831        if matches!(
16832            self.config.dialect,
16833            Some(crate::dialects::DialectType::ClickHouse)
16834        ) {
16835            self.match_identifier("SYNC");
16836            self.match_identifier("NO");
16837            self.match_identifier("DELAY");
16838        }
16839
16840        Ok(Expression::DropTable(Box::new(DropTable {
16841            names,
16842            if_exists,
16843            cascade,
16844            cascade_constraints,
16845            purge,
16846            leading_comments,
16847            object_id_args: None,
16848        })))
16849    }
16850
16851    /// Parse DROP VIEW
16852    fn parse_drop_view(&mut self, materialized: bool) -> Result<Expression> {
16853        self.expect(TokenType::View)?;
16854
16855        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
16856        let name = self.parse_table_ref()?;
16857
16858        // ClickHouse: ON CLUSTER clause
16859        if matches!(
16860            self.config.dialect,
16861            Some(crate::dialects::DialectType::ClickHouse)
16862        ) {
16863            let _ = self.parse_on_cluster_clause()?;
16864            self.match_identifier("SYNC");
16865        }
16866
16867        Ok(Expression::DropView(Box::new(DropView {
16868            name,
16869            if_exists,
16870            materialized,
16871        })))
16872    }
16873
16874    /// Parse DROP INDEX
16875    fn parse_drop_index(&mut self) -> Result<Expression> {
16876        self.expect(TokenType::Index)?;
16877
16878        // PostgreSQL CONCURRENTLY modifier
16879        let concurrently = self.match_identifier("CONCURRENTLY");
16880
16881        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
16882
16883        // Parse potentially qualified index name (a.b.c)
16884        let mut name_parts = vec![self.expect_identifier()?];
16885        while self.match_token(TokenType::Dot) {
16886            name_parts.push(self.expect_identifier()?);
16887        }
16888        let name = Identifier::new(name_parts.join("."));
16889
16890        // Optional ON table
16891        let table = if self.match_token(TokenType::On) {
16892            Some(self.parse_table_ref()?)
16893        } else {
16894            None
16895        };
16896
16897        Ok(Expression::DropIndex(Box::new(DropIndex {
16898            name,
16899            table,
16900            if_exists,
16901            concurrently,
16902        })))
16903    }
16904
16905    /// Parse ALTER statement
16906    fn parse_alter(&mut self) -> Result<Expression> {
16907        self.expect(TokenType::Alter)?;
16908
16909        match self.peek().token_type {
16910            TokenType::Table => {
16911                self.skip();
16912                // Handle IF EXISTS after ALTER TABLE
16913                let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
16914                // Handle PostgreSQL ONLY modifier: ALTER TABLE ONLY "Album" ...
16915                let has_only = self.match_token(TokenType::Only);
16916                let mut name = self.parse_table_ref()?;
16917                if has_only {
16918                    name.only = true;
16919                }
16920
16921                // ClickHouse: ON CLUSTER clause
16922                let on_cluster = self.parse_on_cluster_clause()?;
16923
16924                // Hive: PARTITION(key=value, ...) clause before actions
16925                let partition = if self.match_token(TokenType::Partition) {
16926                    self.expect(TokenType::LParen)?;
16927                    let mut parts = Vec::new();
16928                    loop {
16929                        let key = self.expect_identifier()?;
16930                        self.expect(TokenType::Eq)?;
16931                        let value = self.parse_expression()?;
16932                        parts.push((Identifier::new(key), value));
16933                        if !self.match_token(TokenType::Comma) {
16934                            break;
16935                        }
16936                    }
16937                    self.expect(TokenType::RParen)?;
16938                    Some(parts)
16939                } else {
16940                    None
16941                };
16942
16943                let mut actions = Vec::new();
16944                let mut last_was_add_column = false;
16945                let mut with_check_modifier: Option<String> = None;
16946
16947                loop {
16948                    // Check for MySQL trailing options (ALGORITHM=val, LOCK=val)
16949                    // before trying to parse as a column def or action.
16950                    // The comma before ALGORITHM was consumed at the bottom of the previous iteration.
16951                    if self.check_identifier("ALGORITHM") || self.check_identifier("LOCK") {
16952                        break;
16953                    }
16954
16955                    // TSQL: WITH CHECK / WITH NOCHECK before ADD CONSTRAINT
16956                    if self.check(TokenType::With) {
16957                        let saved = self.current;
16958                        self.skip(); // consume WITH
16959                        if self.check(TokenType::Check) {
16960                            self.skip(); // consume CHECK
16961                            with_check_modifier = Some("WITH CHECK".to_string());
16962                            // Continue to parse the actual action (ADD CONSTRAINT, etc.)
16963                        } else if self.check_identifier("NOCHECK") {
16964                            self.skip(); // consume NOCHECK
16965                            with_check_modifier = Some("WITH NOCHECK".to_string());
16966                            // Continue to parse the actual action (ADD CONSTRAINT, etc.)
16967                        } else {
16968                            // Not WITH CHECK/NOCHECK, restore position
16969                            self.current = saved;
16970                        }
16971                    }
16972
16973                    // If last action was ADD COLUMN and we just saw a comma,
16974                    // check if this is another column definition (not a new action keyword)
16975                    if last_was_add_column
16976                        && !self.check(TokenType::Add)
16977                        && !self.check(TokenType::Drop)
16978                        && !self.check(TokenType::Alter)
16979                        && !self.check(TokenType::Rename)
16980                        && !self.check(TokenType::Set)
16981                        && !self.check_identifier("MODIFY")
16982                        && !self.check(TokenType::Delete)
16983                        && !self.check(TokenType::Update)
16984                        && !self.check_identifier("DETACH")
16985                        && !self.check_identifier("ATTACH")
16986                        && !self.check_identifier("FREEZE")
16987                        && !self.check_identifier("CLEAR")
16988                        && !self.check_identifier("MATERIALIZE")
16989                        && !self.check(TokenType::Comment)
16990                        && !self.check(TokenType::Replace)
16991                        && !self.check_identifier("MOVE")
16992                        && !self.check_identifier("REMOVE")
16993                        && !self.check_identifier("APPLY")
16994                    {
16995                        // Parse additional column definition
16996                        self.match_token(TokenType::Column); // optional COLUMN keyword
16997                        let if_not_exists = self.match_keywords(&[
16998                            TokenType::If,
16999                            TokenType::Not,
17000                            TokenType::Exists,
17001                        ]);
17002                        let col_def = self.parse_column_def()?;
17003                        let position = if self.match_token(TokenType::First) {
17004                            Some(ColumnPosition::First)
17005                        } else if self.match_token(TokenType::After) {
17006                            let after_col = self.expect_identifier()?;
17007                            // ClickHouse: AFTER n.a (dotted nested column name)
17008                            let after_name = if self.match_token(TokenType::Dot) {
17009                                let field = self.expect_identifier()?;
17010                                format!("{}.{}", after_col, field)
17011                            } else {
17012                                after_col
17013                            };
17014                            Some(ColumnPosition::After(Identifier::new(after_name)))
17015                        } else {
17016                            None
17017                        };
17018                        actions.push(AlterTableAction::AddColumn {
17019                            column: col_def,
17020                            if_not_exists,
17021                            position,
17022                        });
17023                        // last_was_add_column remains true
17024                    } else {
17025                        // Check for MySQL trailing options (ALGORITHM=val, LOCK=val)
17026                        // before trying to parse as an action
17027                        if self.check_identifier("ALGORITHM") || self.check_identifier("LOCK") {
17028                            // Retreat one to re-process the comma in the trailing options loop
17029                            self.current -= 1; // back up past the comma consumed in loop
17030                            break;
17031                        }
17032                        let action = self.parse_alter_action()?;
17033                        last_was_add_column = matches!(action, AlterTableAction::AddColumn { .. });
17034                        actions.push(action);
17035                    }
17036                    if !self.match_token(TokenType::Comma) {
17037                        break;
17038                    }
17039                }
17040
17041                // Parse trailing MySQL ALTER TABLE options: ALGORITHM=val, LOCK=val
17042                // These can appear after actions separated by commas (comma already consumed)
17043                // or directly if no actions were parsed
17044                let mut algorithm = None;
17045                let mut lock = None;
17046                loop {
17047                    // First check without consuming comma (comma may have been consumed by action loop)
17048                    if self.check_identifier("ALGORITHM") {
17049                        self.skip();
17050                        self.expect(TokenType::Eq)?;
17051                        algorithm = Some(self.expect_identifier_or_keyword()?.to_ascii_uppercase());
17052                        self.match_token(TokenType::Comma); // optional trailing comma
17053                    } else if self.check_identifier("LOCK") {
17054                        self.skip();
17055                        self.expect(TokenType::Eq)?;
17056                        lock = Some(self.expect_identifier_or_keyword()?.to_ascii_uppercase());
17057                        self.match_token(TokenType::Comma); // optional trailing comma
17058                    } else if self.match_token(TokenType::Comma) {
17059                        // Try after comma
17060                        if self.check_identifier("ALGORITHM") {
17061                            self.skip();
17062                            self.expect(TokenType::Eq)?;
17063                            algorithm = Some(self.expect_identifier_or_keyword()?.to_ascii_uppercase());
17064                        } else if self.check_identifier("LOCK") {
17065                            self.skip();
17066                            self.expect(TokenType::Eq)?;
17067                            lock = Some(self.expect_identifier_or_keyword()?.to_ascii_uppercase());
17068                        } else {
17069                            self.current -= 1;
17070                            break;
17071                        }
17072                    } else {
17073                        break;
17074                    }
17075                }
17076
17077                // ClickHouse: consume optional trailing SETTINGS clause
17078                // e.g., ALTER TABLE t ADD COLUMN c Int64 SETTINGS mutations_sync=2, alter_sync=2
17079                if matches!(
17080                    self.config.dialect,
17081                    Some(crate::dialects::DialectType::ClickHouse)
17082                ) && self.check(TokenType::Settings)
17083                {
17084                    self.skip(); // consume SETTINGS
17085                    let _ = self.parse_settings_property()?;
17086                }
17087
17088                Ok(Expression::AlterTable(Box::new(AlterTable {
17089                    name,
17090                    actions,
17091                    if_exists,
17092                    algorithm,
17093                    lock,
17094                    with_check: with_check_modifier,
17095                    partition,
17096                    on_cluster,
17097                })))
17098            }
17099            TokenType::View => self.parse_alter_view_with_modifiers(None, None, None),
17100            TokenType::Index => self.parse_alter_index(),
17101            TokenType::Sequence => self.parse_alter_sequence(),
17102            _ if self.check_identifier("SESSION") => {
17103                // ALTER SESSION SET/UNSET (Snowflake)
17104                self.skip(); // consume SESSION
17105                match self.parse_alter_session()? {
17106                    Some(expr) => Ok(expr),
17107                    None => {
17108                        // Fall back to command
17109                        Ok(Expression::Command(Box::new(Command {
17110                            this: "ALTER SESSION".to_string(),
17111                        })))
17112                    }
17113                }
17114            }
17115            _ => {
17116                // MySQL: ALTER ALGORITHM = val VIEW, ALTER DEFINER = val VIEW,
17117                // ALTER SQL SECURITY = val VIEW
17118                let mut view_algorithm = None;
17119                let mut view_definer = None;
17120                let mut view_sql_security = None;
17121
17122                loop {
17123                    if self.check_identifier("ALGORITHM") {
17124                        self.skip();
17125                        self.expect(TokenType::Eq)?;
17126                        view_algorithm = Some(self.expect_identifier_or_keyword()?.to_ascii_uppercase());
17127                    } else if self.check_identifier("DEFINER") {
17128                        self.skip();
17129                        self.expect(TokenType::Eq)?;
17130                        // Parse user@host format: 'admin'@'localhost'
17131                        let mut definer_str = String::new();
17132                        if self.check(TokenType::String) {
17133                            definer_str.push_str(&format!("'{}'", self.advance().text));
17134                        } else {
17135                            definer_str.push_str(&self.expect_identifier_or_keyword()?);
17136                        }
17137                        // Check for @ separator
17138                        if !self.is_at_end() && self.peek().text == "@" {
17139                            definer_str.push_str(&self.advance().text);
17140                            if self.check(TokenType::String) {
17141                                definer_str.push_str(&format!("'{}'", self.advance().text));
17142                            } else if !self.is_at_end() {
17143                                definer_str.push_str(&self.advance().text);
17144                            }
17145                        }
17146                        view_definer = Some(definer_str);
17147                    } else if self.check_identifier("SQL") {
17148                        self.skip();
17149                        if self.match_identifier("SECURITY") {
17150                            self.match_token(TokenType::Eq);
17151                            view_sql_security =
17152                                Some(self.expect_identifier_or_keyword()?.to_ascii_uppercase());
17153                        }
17154                    } else {
17155                        break;
17156                    }
17157                }
17158
17159                if self.check(TokenType::View) {
17160                    self.parse_alter_view_with_modifiers(
17161                        view_algorithm,
17162                        view_definer,
17163                        view_sql_security,
17164                    )
17165                } else {
17166                    // Fall back to Raw for unrecognized ALTER targets
17167                    let start = self.current;
17168                    while !self.is_at_end() && !self.check(TokenType::Semicolon) {
17169                        self.skip();
17170                    }
17171                    let sql = self.tokens_to_sql(start, self.current);
17172                    Ok(Expression::Raw(Raw {
17173                        sql: format!("ALTER {}", sql),
17174                    }))
17175                }
17176            }
17177        }
17178    }
17179
17180    /// Parse ALTER TABLE action
17181    fn parse_alter_action(&mut self) -> Result<AlterTableAction> {
17182        if self.match_token(TokenType::Add) {
17183            // ClickHouse: ADD INDEX idx expr TYPE minmax GRANULARITY 1
17184            // ClickHouse: ADD PROJECTION name (SELECT ...)
17185            // ClickHouse: ADD STATISTICS col1, col2 TYPE tdigest, uniq
17186            // These have different syntax from MySQL ADD INDEX, so consume as Raw
17187            if matches!(
17188                self.config.dialect,
17189                Some(crate::dialects::DialectType::ClickHouse)
17190            ) && (self.check(TokenType::Index)
17191                || self.check_identifier("PROJECTION")
17192                || self.check_identifier("STATISTICS"))
17193            {
17194                let is_statistics = self.check_identifier("STATISTICS");
17195                let mut tokens: Vec<(String, TokenType)> =
17196                    vec![("ADD".to_string(), TokenType::Add)];
17197                let mut paren_depth = 0i32;
17198                while !self.is_at_end() && !self.check(TokenType::Semicolon) {
17199                    // STATISTICS uses commas internally (col1, col2 TYPE t1, t2), don't break at comma
17200                    if self.check(TokenType::Comma) && paren_depth == 0 && !is_statistics {
17201                        break;
17202                    }
17203                    let token = self.advance();
17204                    if token.token_type == TokenType::LParen {
17205                        paren_depth += 1;
17206                    }
17207                    if token.token_type == TokenType::RParen {
17208                        paren_depth -= 1;
17209                    }
17210                    let text = if token.token_type == TokenType::QuotedIdentifier {
17211                        format!("\"{}\"", token.text)
17212                    } else if token.token_type == TokenType::String {
17213                        format!("'{}'", token.text)
17214                    } else {
17215                        token.text.clone()
17216                    };
17217                    tokens.push((text, token.token_type));
17218                }
17219                return Ok(AlterTableAction::Raw {
17220                    sql: self.join_command_tokens(tokens),
17221                });
17222            }
17223            // ADD CONSTRAINT or ADD COLUMN or ADD INDEX
17224            if self.match_token(TokenType::Constraint) {
17225                // ADD CONSTRAINT name ...
17226                let name = Some(self.expect_identifier_with_quoted()?);
17227                let constraint = self.parse_constraint_definition(name)?;
17228                Ok(AlterTableAction::AddConstraint(constraint))
17229            } else if self.check(TokenType::PrimaryKey)
17230                || self.check(TokenType::ForeignKey)
17231                || self.check(TokenType::Check)
17232            {
17233                // ADD PRIMARY KEY / FOREIGN KEY / CHECK (without CONSTRAINT keyword)
17234                let constraint = self.parse_table_constraint()?;
17235                Ok(AlterTableAction::AddConstraint(constraint))
17236            } else if self.check(TokenType::Index)
17237                || self.check(TokenType::Key)
17238                || self.check(TokenType::Unique)
17239                || self.check_identifier("FULLTEXT")
17240                || self.check_identifier("SPATIAL")
17241            {
17242                // ADD [UNIQUE|FULLTEXT|SPATIAL] [{INDEX|KEY}] [name] (columns) [USING {BTREE|HASH}]
17243                let kind = if self.match_token(TokenType::Unique) {
17244                    Some("UNIQUE".to_string())
17245                } else if self.match_identifier("FULLTEXT") {
17246                    Some("FULLTEXT".to_string())
17247                } else if self.match_identifier("SPATIAL") {
17248                    Some("SPATIAL".to_string())
17249                } else {
17250                    None
17251                };
17252                // Consume optional INDEX or KEY keyword, track which was used
17253                let use_key_keyword = if self.match_token(TokenType::Key) {
17254                    true
17255                } else {
17256                    self.match_token(TokenType::Index);
17257                    false
17258                };
17259
17260                // Optional index name (before the columns)
17261                let name = if !self.check(TokenType::LParen) && !self.check(TokenType::Using) {
17262                    Some(self.expect_identifier_with_quoted()?)
17263                } else {
17264                    None
17265                };
17266
17267                // Parse columns (with optional prefix length and DESC)
17268                self.expect(TokenType::LParen)?;
17269                let columns = self.parse_index_identifier_list()?;
17270                self.expect(TokenType::RParen)?;
17271
17272                // Parse optional USING BTREE|HASH
17273                let modifiers = self.parse_constraint_modifiers();
17274
17275                Ok(AlterTableAction::AddConstraint(TableConstraint::Index {
17276                    name,
17277                    columns,
17278                    kind,
17279                    modifiers,
17280                    use_key_keyword,
17281                    expression: None,
17282                    index_type: None,
17283                    granularity: None,
17284                }))
17285            } else if self.match_identifier("COLUMNS") {
17286                // ADD COLUMNS (col1 TYPE, col2 TYPE, ...) [CASCADE] - Hive/Spark syntax
17287                self.expect(TokenType::LParen)?;
17288                let mut columns = Vec::new();
17289                loop {
17290                    let col_def = self.parse_column_def()?;
17291                    columns.push(col_def);
17292                    if !self.match_token(TokenType::Comma) {
17293                        break;
17294                    }
17295                }
17296                self.expect(TokenType::RParen)?;
17297                let cascade = self.match_token(TokenType::Cascade);
17298                Ok(AlterTableAction::AddColumns { columns, cascade })
17299            } else if self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists]) {
17300                // ADD IF NOT EXISTS PARTITION(key = value) - Hive/Spark syntax
17301                // ADD IF NOT EXISTS col1 INT, col2 INT - Snowflake syntax
17302                if self.match_token(TokenType::Partition) {
17303                    self.expect(TokenType::LParen)?;
17304                    let mut partition_exprs = Vec::new();
17305                    loop {
17306                        if let Some(expr) = self.parse_conjunction()? {
17307                            partition_exprs.push(expr);
17308                        }
17309                        if !self.match_token(TokenType::Comma) {
17310                            break;
17311                        }
17312                    }
17313                    self.expect(TokenType::RParen)?;
17314                    let partition =
17315                        Expression::Partition(Box::new(crate::expressions::Partition {
17316                            expressions: partition_exprs,
17317                            subpartition: false,
17318                        }));
17319                    let location = if self.match_text_seq(&["LOCATION"]) {
17320                        self.parse_property()?
17321                    } else {
17322                        None
17323                    };
17324                    return Ok(AlterTableAction::AddPartition {
17325                        partition,
17326                        if_not_exists: true,
17327                        location,
17328                    });
17329                } else {
17330                    // Snowflake: ADD IF NOT EXISTS col1 INT, [IF NOT EXISTS] col2 INT
17331                    // Parse just the first column; the caller's comma loop handles the rest
17332                    let col_def = self.parse_column_def()?;
17333                    return Ok(AlterTableAction::AddColumn {
17334                        column: col_def,
17335                        if_not_exists: true,
17336                        position: None,
17337                    });
17338                }
17339            } else if self.check(TokenType::Partition) {
17340                // ADD PARTITION(key = value) - Hive/Spark syntax
17341                self.skip(); // consume PARTITION
17342                self.expect(TokenType::LParen)?;
17343                let mut partition_exprs = Vec::new();
17344                loop {
17345                    if let Some(expr) = self.parse_conjunction()? {
17346                        partition_exprs.push(expr);
17347                    }
17348                    if !self.match_token(TokenType::Comma) {
17349                        break;
17350                    }
17351                }
17352                self.expect(TokenType::RParen)?;
17353                let partition = Expression::Partition(Box::new(crate::expressions::Partition {
17354                    expressions: partition_exprs,
17355                    subpartition: false,
17356                }));
17357                let location = if self.match_text_seq(&["LOCATION"]) {
17358                    // Parse the LOCATION value (typically a string literal like 'path')
17359                    Some(self.parse_primary()?)
17360                } else {
17361                    None
17362                };
17363                Ok(AlterTableAction::AddPartition {
17364                    partition,
17365                    if_not_exists: false,
17366                    location,
17367                })
17368            } else {
17369                // ADD COLUMN or ADD (col1 TYPE, col2 TYPE) for Oracle
17370                let has_column_keyword = self.match_token(TokenType::Column); // optional COLUMN keyword
17371
17372                // Check for Oracle-style ADD (col1 TYPE, col2 TYPE, ...) without COLUMN keyword
17373                if !has_column_keyword && self.check(TokenType::LParen) {
17374                    // Oracle multi-column ADD syntax: ADD (col1 TYPE, col2 TYPE, ...)
17375                    self.skip(); // consume '('
17376                    let mut columns = Vec::new();
17377                    loop {
17378                        let col_def = self.parse_column_def()?;
17379                        columns.push(col_def);
17380                        if !self.match_token(TokenType::Comma) {
17381                            break;
17382                        }
17383                    }
17384                    self.expect(TokenType::RParen)?;
17385                    // Use AddColumns with cascade=false for Oracle syntax
17386                    Ok(AlterTableAction::AddColumns {
17387                        columns,
17388                        cascade: false,
17389                    })
17390                } else {
17391                    // Handle IF NOT EXISTS for ADD COLUMN
17392                    let if_not_exists =
17393                        self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists]);
17394                    let col_def = self.parse_column_def()?;
17395                    // Check for FIRST or AFTER position modifiers (MySQL/MariaDB)
17396                    let position = if self.match_token(TokenType::First) {
17397                        Some(ColumnPosition::First)
17398                    } else if self.match_token(TokenType::After) {
17399                        let after_col = self.expect_identifier()?;
17400                        // ClickHouse: AFTER n.a (dotted nested column name)
17401                        let after_name = if self.match_token(TokenType::Dot) {
17402                            let field = self.expect_identifier()?;
17403                            format!("{}.{}", after_col, field)
17404                        } else {
17405                            after_col
17406                        };
17407                        Some(ColumnPosition::After(Identifier::new(after_name)))
17408                    } else {
17409                        None
17410                    };
17411                    Ok(AlterTableAction::AddColumn {
17412                        column: col_def,
17413                        if_not_exists,
17414                        position,
17415                    })
17416                }
17417            }
17418        } else if self.match_token(TokenType::Drop) {
17419            // ClickHouse: DROP INDEX idx, DROP PROJECTION name, DROP STATISTICS, etc.
17420            // These have different syntax from MySQL, so consume as Raw
17421            if matches!(
17422                self.config.dialect,
17423                Some(crate::dialects::DialectType::ClickHouse)
17424            ) && (self.check(TokenType::Index)
17425                || self.check_identifier("PROJECTION")
17426                || self.check_identifier("STATISTICS")
17427                || self.check_identifier("DETACHED")
17428                || self.check_identifier("PART"))
17429            {
17430                let is_statistics = self.check_identifier("STATISTICS");
17431                let mut tokens: Vec<(String, TokenType)> =
17432                    vec![("DROP".to_string(), TokenType::Drop)];
17433                let mut paren_depth = 0i32;
17434                while !self.is_at_end() && !self.check(TokenType::Semicolon) {
17435                    if self.check(TokenType::Comma) && paren_depth == 0 && !is_statistics {
17436                        break;
17437                    }
17438                    let token = self.advance();
17439                    if token.token_type == TokenType::LParen {
17440                        paren_depth += 1;
17441                    }
17442                    if token.token_type == TokenType::RParen {
17443                        paren_depth -= 1;
17444                    }
17445                    let text = if token.token_type == TokenType::QuotedIdentifier {
17446                        format!("\"{}\"", token.text)
17447                    } else if token.token_type == TokenType::String {
17448                        format!("'{}'", token.text)
17449                    } else {
17450                        token.text.clone()
17451                    };
17452                    tokens.push((text, token.token_type));
17453                }
17454                return Ok(AlterTableAction::Raw {
17455                    sql: self.join_command_tokens(tokens),
17456                });
17457            }
17458            // Handle IF EXISTS before determining what to drop
17459            let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
17460
17461            if self.match_token(TokenType::Partition) {
17462                // DROP [IF EXISTS] PARTITION expr [, PARTITION expr ...]
17463                // ClickHouse supports: PARTITION 201901, PARTITION ALL,
17464                // PARTITION tuple(...), PARTITION ID '...'
17465                let mut partitions = Vec::new();
17466                loop {
17467                    if self.check(TokenType::LParen) {
17468                        // ClickHouse: PARTITION (expr) or PARTITION (expr, expr, ...)
17469                        // Standard SQL: PARTITION (key=value, ...)
17470                        // Peek ahead: if LParen is followed by String/Number (not identifier=),
17471                        // parse as expression
17472                        let is_ch_expr = matches!(
17473                            self.config.dialect,
17474                            Some(crate::dialects::DialectType::ClickHouse)
17475                        ) && self.current + 1 < self.tokens.len()
17476                            && (self.tokens[self.current + 1].token_type == TokenType::String
17477                                || self.tokens[self.current + 1].token_type == TokenType::Number
17478                                || self.tokens[self.current + 1].token_type == TokenType::LParen
17479                                || (self.current + 2 < self.tokens.len()
17480                                    && self.tokens[self.current + 2].token_type != TokenType::Eq));
17481                        if is_ch_expr {
17482                            // Parse as tuple expression
17483                            let expr = self.parse_expression()?;
17484                            partitions.push(vec![(Identifier::new("__expr__".to_string()), expr)]);
17485                        } else {
17486                            self.skip(); // consume (
17487                            let mut parts = Vec::new();
17488                            loop {
17489                                let key = self.expect_identifier()?;
17490                                self.expect(TokenType::Eq)?;
17491                                let value = self.parse_expression()?;
17492                                parts.push((Identifier::new(key), value));
17493                                if !self.match_token(TokenType::Comma) {
17494                                    break;
17495                                }
17496                            }
17497                            self.expect(TokenType::RParen)?;
17498                            partitions.push(parts);
17499                        }
17500                    } else if self.match_text_seq(&["ALL"]) {
17501                        // ClickHouse: PARTITION ALL
17502                        partitions.push(vec![(
17503                            Identifier::new("ALL".to_string()),
17504                            Expression::Boolean(BooleanLiteral { value: true }),
17505                        )]);
17506                    } else if self.match_text_seq(&["ID"]) {
17507                        // ClickHouse: PARTITION ID 'string'
17508                        let id_val = self.parse_expression()?;
17509                        partitions.push(vec![(Identifier::new("ID".to_string()), id_val)]);
17510                    } else {
17511                        // ClickHouse: PARTITION <expression> (number, tuple(...), etc.)
17512                        let expr = self.parse_expression()?;
17513                        partitions.push(vec![(Identifier::new("__expr__".to_string()), expr)]);
17514                    }
17515                    // Check for ", PARTITION" for multiple partitions
17516                    if self.match_token(TokenType::Comma) {
17517                        if !self.match_token(TokenType::Partition) {
17518                            break;
17519                        }
17520                    } else {
17521                        break;
17522                    }
17523                }
17524                Ok(AlterTableAction::DropPartition {
17525                    partitions,
17526                    if_exists,
17527                })
17528            } else if self.match_token(TokenType::Column) {
17529                // DROP [IF EXISTS] COLUMN [IF EXISTS] name [CASCADE]
17530                // Check for IF EXISTS after COLUMN as well
17531                let if_exists =
17532                    if_exists || self.match_keywords(&[TokenType::If, TokenType::Exists]);
17533                let mut name = self.expect_identifier_with_quoted()?;
17534                // ClickHouse: nested column names like n.ui8
17535                if matches!(
17536                    self.config.dialect,
17537                    Some(crate::dialects::DialectType::ClickHouse)
17538                ) && self.match_token(TokenType::Dot)
17539                {
17540                    let sub = self.expect_identifier_with_quoted()?;
17541                    name.name = format!("{}.{}", name.name, sub.name);
17542                }
17543                let cascade = self.match_token(TokenType::Cascade);
17544                Ok(AlterTableAction::DropColumn {
17545                    name,
17546                    if_exists,
17547                    cascade,
17548                })
17549            } else if self.match_token(TokenType::Constraint) {
17550                // DROP [IF EXISTS] CONSTRAINT name
17551                let name = self.expect_identifier_with_quoted()?;
17552                Ok(AlterTableAction::DropConstraint { name, if_exists })
17553            } else if self.match_keywords(&[TokenType::ForeignKey, TokenType::Key]) {
17554                // DROP FOREIGN KEY name (Oracle/MySQL)
17555                let name = self.expect_identifier_with_quoted()?;
17556                Ok(AlterTableAction::DropForeignKey { name })
17557            } else if self.check_identifier("COLUMNS") && self.check_next(TokenType::LParen) {
17558                // DROP COLUMNS (col1, col2, ...) - Spark/Databricks syntax
17559                self.skip(); // consume COLUMNS
17560                self.expect(TokenType::LParen)?;
17561                let mut names = Vec::new();
17562                loop {
17563                    let name = self.expect_identifier_with_quoted()?;
17564                    names.push(name);
17565                    if !self.match_token(TokenType::Comma) {
17566                        break;
17567                    }
17568                }
17569                self.expect(TokenType::RParen)?;
17570                Ok(AlterTableAction::DropColumns { names })
17571            } else {
17572                // DROP [IF EXISTS] name (implicit column) [CASCADE]
17573                let mut name = self.expect_identifier_with_quoted()?;
17574                // ClickHouse: nested column names like n.ui8
17575                if matches!(
17576                    self.config.dialect,
17577                    Some(crate::dialects::DialectType::ClickHouse)
17578                ) && self.match_token(TokenType::Dot)
17579                {
17580                    let sub = self.expect_identifier_with_quoted()?;
17581                    name.name = format!("{}.{}", name.name, sub.name);
17582                }
17583                let cascade = self.match_token(TokenType::Cascade);
17584                Ok(AlterTableAction::DropColumn {
17585                    name,
17586                    if_exists,
17587                    cascade,
17588                })
17589            }
17590        } else if self.match_token(TokenType::Rename) {
17591            if self.match_token(TokenType::Column) {
17592                // RENAME COLUMN [IF EXISTS] old TO new
17593                let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
17594                let mut old_name = self.expect_identifier_or_safe_keyword_with_quoted()?;
17595                // ClickHouse: nested column names like n.x
17596                if matches!(
17597                    self.config.dialect,
17598                    Some(crate::dialects::DialectType::ClickHouse)
17599                ) && self.match_token(TokenType::Dot)
17600                {
17601                    let field = self.expect_identifier_with_quoted()?;
17602                    old_name = Identifier {
17603                        name: format!("{}.{}", old_name.name, field.name),
17604                        quoted: false,
17605                        trailing_comments: Vec::new(),
17606                        span: None,
17607                    };
17608                }
17609                self.expect(TokenType::To)?;
17610                let mut new_name = self.expect_identifier_or_safe_keyword_with_quoted()?;
17611                // ClickHouse: nested column names like n.y
17612                if matches!(
17613                    self.config.dialect,
17614                    Some(crate::dialects::DialectType::ClickHouse)
17615                ) && self.match_token(TokenType::Dot)
17616                {
17617                    let field = self.expect_identifier_or_safe_keyword_with_quoted()?;
17618                    new_name = Identifier {
17619                        name: format!("{}.{}", new_name.name, field.name),
17620                        quoted: false,
17621                        trailing_comments: Vec::new(),
17622                        span: None,
17623                    };
17624                }
17625                Ok(AlterTableAction::RenameColumn {
17626                    old_name,
17627                    new_name,
17628                    if_exists,
17629                })
17630            } else if self.match_token(TokenType::To) {
17631                // RENAME TO new_table
17632                let new_name = self.parse_table_ref()?;
17633                Ok(AlterTableAction::RenameTable(new_name))
17634            } else if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
17635                // StarRocks/Doris: RENAME new_name (without TO)
17636                // SQLite: RENAME old_name TO new_name (without COLUMN keyword)
17637                let first_name = self.expect_identifier_with_quoted()?;
17638                if self.match_token(TokenType::To) {
17639                    let new_name = self.expect_identifier_with_quoted()?;
17640                    Ok(AlterTableAction::RenameColumn {
17641                        old_name: first_name,
17642                        new_name,
17643                        if_exists: false,
17644                    })
17645                } else {
17646                    // No TO keyword: treat as RENAME TABLE (StarRocks/Doris)
17647                    Ok(AlterTableAction::RenameTable(TableRef::new(
17648                        first_name.name,
17649                    )))
17650                }
17651            } else {
17652                Err(self.parse_error("Expected COLUMN or TO after RENAME"))
17653            }
17654        } else if self.match_token(TokenType::Alter) {
17655            // Check for ALTER INDEX (MySQL: ALTER TABLE t ALTER INDEX i VISIBLE/INVISIBLE)
17656            if self.match_token(TokenType::Index) {
17657                let name = self.expect_identifier_with_quoted()?;
17658                let visible = if self.match_identifier("VISIBLE") {
17659                    true
17660                } else if self.match_identifier("INVISIBLE") {
17661                    false
17662                } else {
17663                    return Err(
17664                        self.parse_error("Expected VISIBLE or INVISIBLE after ALTER INDEX name")
17665                    );
17666                };
17667                Ok(AlterTableAction::AlterIndex { name, visible })
17668            } else if self.check_identifier("SORTKEY") {
17669                // Redshift: ALTER TABLE t ALTER SORTKEY AUTO|NONE|(col1, col2)
17670                self.skip(); // consume SORTKEY
17671                if self.match_texts(&["AUTO", "NONE"]) {
17672                    let style = self.previous().text.to_ascii_uppercase();
17673                    Ok(AlterTableAction::AlterSortKey {
17674                        this: Some(style),
17675                        expressions: Vec::new(),
17676                        compound: false,
17677                    })
17678                } else if self.check(TokenType::LParen) {
17679                    // (col1, col2) syntax
17680                    let wrapped = self.parse_wrapped_id_vars()?;
17681                    let expressions = if let Some(Expression::Tuple(t)) = wrapped {
17682                        t.expressions
17683                    } else {
17684                        Vec::new()
17685                    };
17686                    Ok(AlterTableAction::AlterSortKey {
17687                        this: None,
17688                        expressions,
17689                        compound: false,
17690                    })
17691                } else {
17692                    Err(self.parse_error("Expected AUTO, NONE, or (columns) after SORTKEY"))
17693                }
17694            } else if self.check_identifier("COMPOUND") {
17695                // Redshift: ALTER TABLE t ALTER COMPOUND SORTKEY (col1, col2)
17696                self.skip(); // consume COMPOUND
17697                if !self.match_identifier("SORTKEY") {
17698                    return Err(self.parse_error("Expected SORTKEY after COMPOUND"));
17699                }
17700                if self.check(TokenType::LParen) {
17701                    let wrapped = self.parse_wrapped_id_vars()?;
17702                    let expressions = if let Some(Expression::Tuple(t)) = wrapped {
17703                        t.expressions
17704                    } else {
17705                        Vec::new()
17706                    };
17707                    Ok(AlterTableAction::AlterSortKey {
17708                        this: None,
17709                        expressions,
17710                        compound: true,
17711                    })
17712                } else {
17713                    Err(self.parse_error("Expected (columns) after COMPOUND SORTKEY"))
17714                }
17715            } else if self.check_identifier("DISTSTYLE") {
17716                // Redshift: ALTER TABLE t ALTER DISTSTYLE ALL|EVEN|AUTO|KEY [DISTKEY col]
17717                self.skip(); // consume DISTSTYLE
17718                if self.match_texts(&["ALL", "EVEN", "AUTO"]) {
17719                    let style = self.previous().text.to_ascii_uppercase();
17720                    Ok(AlterTableAction::AlterDistStyle {
17721                        style,
17722                        distkey: None,
17723                    })
17724                } else if self.match_token(TokenType::Key) || self.match_identifier("KEY") {
17725                    // DISTSTYLE KEY DISTKEY col
17726                    if !self.match_identifier("DISTKEY") {
17727                        return Err(self.parse_error("Expected DISTKEY after DISTSTYLE KEY"));
17728                    }
17729                    let col = self.expect_identifier_with_quoted()?;
17730                    Ok(AlterTableAction::AlterDistStyle {
17731                        style: "KEY".to_string(),
17732                        distkey: Some(col),
17733                    })
17734                } else {
17735                    Err(self.parse_error("Expected ALL, EVEN, AUTO, or KEY after DISTSTYLE"))
17736                }
17737            } else if self.check_identifier("DISTKEY") {
17738                // Redshift: ALTER TABLE t ALTER DISTKEY col (shorthand for DISTSTYLE KEY DISTKEY col)
17739                self.skip(); // consume DISTKEY
17740                let col = self.expect_identifier_with_quoted()?;
17741                Ok(AlterTableAction::AlterDistStyle {
17742                    style: "KEY".to_string(),
17743                    distkey: Some(col),
17744                })
17745            } else {
17746                // ALTER COLUMN
17747                self.match_token(TokenType::Column); // optional COLUMN keyword
17748                let name = self.expect_identifier_with_quoted()?;
17749                let action = self.parse_alter_column_action()?;
17750                Ok(AlterTableAction::AlterColumn {
17751                    name,
17752                    action,
17753                    use_modify_keyword: false,
17754                })
17755            }
17756        } else if self.match_identifier("MODIFY") {
17757            // ClickHouse: MODIFY ORDER BY, MODIFY SETTING, MODIFY TTL, MODIFY QUERY,
17758            // MODIFY COLUMN name type [DEFAULT|MATERIALIZED|ALIAS] [CODEC] [TTL] [COMMENT], etc.
17759            // These are ClickHouse-specific and have richer syntax than MySQL MODIFY COLUMN.
17760            // Consume all ClickHouse MODIFY actions as Raw.
17761            if matches!(
17762                self.config.dialect,
17763                Some(crate::dialects::DialectType::ClickHouse)
17764            ) {
17765                // MODIFY SETTING uses commas between settings (not action separators)
17766                let is_setting =
17767                    self.check(TokenType::Settings) || self.check_identifier("SETTING");
17768                let mut tokens: Vec<(String, TokenType)> =
17769                    vec![("MODIFY".to_string(), TokenType::Var)];
17770                let mut paren_depth = 0i32;
17771                while !self.is_at_end() && !self.check(TokenType::Semicolon) {
17772                    if self.check(TokenType::Comma) && paren_depth == 0 && !is_setting {
17773                        break;
17774                    }
17775                    let token = self.advance();
17776                    if token.token_type == TokenType::LParen {
17777                        paren_depth += 1;
17778                    }
17779                    if token.token_type == TokenType::RParen {
17780                        paren_depth -= 1;
17781                    }
17782                    let text = if token.token_type == TokenType::QuotedIdentifier {
17783                        format!("\"{}\"", token.text)
17784                    } else if token.token_type == TokenType::String {
17785                        format!("'{}'", token.text)
17786                    } else {
17787                        token.text.clone()
17788                    };
17789                    tokens.push((text, token.token_type));
17790                }
17791                return Ok(AlterTableAction::Raw {
17792                    sql: self.join_command_tokens(tokens),
17793                });
17794            }
17795            // MODIFY COLUMN (MySQL syntax for altering column type)
17796            self.match_token(TokenType::Column); // optional COLUMN keyword
17797            let name = Identifier::new(self.expect_identifier()?);
17798            // Parse the data type directly (MySQL MODIFY COLUMN col TYPE)
17799            let data_type = self.parse_data_type()?;
17800            // Parse optional COLLATE clause
17801            let collate = if self.match_token(TokenType::Collate) {
17802                if self.check(TokenType::String) {
17803                    Some(self.advance().text)
17804                } else if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
17805                    Some(self.advance().text)
17806                } else {
17807                    None
17808                }
17809            } else {
17810                None
17811            };
17812            Ok(AlterTableAction::AlterColumn {
17813                name,
17814                action: AlterColumnAction::SetDataType {
17815                    data_type,
17816                    using: None,
17817                    collate,
17818                },
17819                use_modify_keyword: true,
17820            })
17821        } else if self.match_identifier("CHANGE") {
17822            // CHANGE [COLUMN] old_name new_name [data_type] [COMMENT 'comment'] - Hive/MySQL/SingleStore syntax
17823            // In SingleStore, data_type can be omitted for simple renames
17824            self.match_token(TokenType::Column); // optional COLUMN keyword
17825            let old_name = Identifier::new(self.expect_identifier()?);
17826            let new_name = Identifier::new(self.expect_identifier()?);
17827            // Try to parse data type - it's optional in SingleStore
17828            let data_type = if !self.is_at_end()
17829                && !self.check(TokenType::Comment)
17830                && !self.check(TokenType::Comma)
17831                && !self.check(TokenType::Semicolon)
17832            {
17833                // Check if next token could start a data type
17834                let tok = self.peek();
17835                if tok.token_type.is_keyword()
17836                    || tok.token_type == TokenType::Identifier
17837                    || tok.token_type == TokenType::Var
17838                {
17839                    Some(self.parse_data_type()?)
17840                } else {
17841                    None
17842                }
17843            } else {
17844                None
17845            };
17846            let comment = if self.match_token(TokenType::Comment) {
17847                Some(self.expect_string()?)
17848            } else {
17849                None
17850            };
17851            let cascade = self.match_text_seq(&["CASCADE"]);
17852            // Also check for RESTRICT (the opposite, just consume it)
17853            if !cascade {
17854                self.match_text_seq(&["RESTRICT"]);
17855            }
17856            Ok(AlterTableAction::ChangeColumn {
17857                old_name,
17858                new_name,
17859                data_type,
17860                comment,
17861                cascade,
17862            })
17863        } else if self.match_token(TokenType::Constraint) {
17864            // CONSTRAINT name ... (implicit ADD, CONSTRAINT already consumed)
17865            // Parse the constraint name and then the constraint definition
17866            let name = Some(self.expect_identifier_with_quoted()?);
17867            let constraint = self.parse_constraint_definition(name)?;
17868            Ok(AlterTableAction::AddConstraint(constraint))
17869        } else if self.check(TokenType::PrimaryKey)
17870            || self.check(TokenType::ForeignKey)
17871            || self.check(TokenType::Unique)
17872        {
17873            // ADD CONSTRAINT (implicit ADD, no CONSTRAINT keyword)
17874            let constraint = self.parse_table_constraint()?;
17875            Ok(AlterTableAction::AddConstraint(constraint))
17876        } else if self.match_token(TokenType::Delete) {
17877            // ALTER TABLE t DELETE WHERE x = 1 (BigQuery syntax)
17878            self.expect(TokenType::Where)?;
17879            let where_clause = self.parse_expression()?;
17880            Ok(AlterTableAction::Delete { where_clause })
17881        } else if self.match_keyword("SWAP") {
17882            // Snowflake: ALTER TABLE a SWAP WITH b
17883            self.expect(TokenType::With)?;
17884            let target = self.parse_table_ref()?;
17885            Ok(AlterTableAction::SwapWith(target))
17886        } else if self.match_token(TokenType::Set) {
17887            // TSQL: ALTER TABLE t SET (SYSTEM_VERSIONING=ON, DATA_DELETION=ON, ...)
17888            if self.check(TokenType::LParen) {
17889                self.skip(); // consume (
17890                let mut expressions = Vec::new();
17891                loop {
17892                    if self.check(TokenType::RParen) {
17893                        break;
17894                    }
17895                    if self.check_identifier("SYSTEM_VERSIONING") {
17896                        let expr = self.parse_system_versioning_option()?;
17897                        expressions.push(expr);
17898                    } else if self.check_identifier("DATA_DELETION") {
17899                        let expr = self.parse_data_deletion_option()?;
17900                        expressions.push(expr);
17901                    } else {
17902                        // Generic key=value (e.g., FILESTREAM_ON = 'test')
17903                        let expr = self.parse_expression()?;
17904                        expressions.push(expr);
17905                    }
17906                    if !self.match_token(TokenType::Comma) {
17907                        break;
17908                    }
17909                }
17910                self.expect(TokenType::RParen)?;
17911                Ok(AlterTableAction::SetOptions { expressions })
17912            } else if self.match_keyword("TAG") {
17913                // Snowflake: SET TAG key='value', ... (key can be qualified like schema.tagname)
17914                let mut tags = Vec::new();
17915                loop {
17916                    // Parse qualified tag name (e.g., foo.bar or just bar)
17917                    let mut key = self.expect_identifier_or_keyword()?;
17918                    while self.match_token(TokenType::Dot) {
17919                        let next = self.expect_identifier_or_keyword()?;
17920                        key = format!("{}.{}", key, next);
17921                    }
17922                    self.expect(TokenType::Eq)?;
17923                    let value = self.parse_primary()?;
17924                    tags.push((key, value));
17925                    if !self.match_token(TokenType::Comma) {
17926                        break;
17927                    }
17928                }
17929                Ok(AlterTableAction::SetTag { expressions: tags })
17930            } else if self.check_identifier("LOGGED") {
17931                // PostgreSQL: ALTER TABLE t SET LOGGED
17932                self.skip();
17933                Ok(AlterTableAction::SetAttribute {
17934                    attribute: "LOGGED".to_string(),
17935                })
17936            } else if self.check_identifier("UNLOGGED") {
17937                // PostgreSQL: ALTER TABLE t SET UNLOGGED
17938                self.skip();
17939                Ok(AlterTableAction::SetAttribute {
17940                    attribute: "UNLOGGED".to_string(),
17941                })
17942            } else if self.match_identifier("WITHOUT") {
17943                // PostgreSQL: ALTER TABLE t SET WITHOUT CLUSTER/OIDS
17944                let what = self.expect_identifier_or_keyword()?;
17945                Ok(AlterTableAction::SetAttribute {
17946                    attribute: format!("WITHOUT {}", what),
17947                })
17948            } else if self.check_identifier("ACCESS") {
17949                // PostgreSQL: ALTER TABLE t SET ACCESS METHOD method
17950                self.skip();
17951                // Consume "METHOD"
17952                if !self.match_identifier("METHOD") {
17953                    return Err(self.parse_error("Expected METHOD after ACCESS"));
17954                }
17955                let method = self.expect_identifier_or_keyword()?;
17956                Ok(AlterTableAction::SetAttribute {
17957                    attribute: format!("ACCESS METHOD {}", method),
17958                })
17959            } else if self.check_identifier("TABLESPACE") {
17960                // PostgreSQL: ALTER TABLE t SET TABLESPACE tablespace
17961                self.skip();
17962                let name = self.expect_identifier_or_keyword()?;
17963                Ok(AlterTableAction::SetAttribute {
17964                    attribute: format!("TABLESPACE {}", name),
17965                })
17966            } else if self.check_identifier("STAGE_FILE_FORMAT") {
17967                // Snowflake: ALTER TABLE t SET STAGE_FILE_FORMAT = (options)
17968                self.skip();
17969                let options = self.parse_wrapped_options()?;
17970                Ok(AlterTableAction::SetStageFileFormat { options })
17971            } else if self.check_identifier("STAGE_COPY_OPTIONS") {
17972                // Snowflake: ALTER TABLE t SET STAGE_COPY_OPTIONS = (options)
17973                self.skip();
17974                let options = self.parse_wrapped_options()?;
17975                Ok(AlterTableAction::SetStageCopyOptions { options })
17976            } else if self.match_token(TokenType::Authorization) {
17977                // Trino: ALTER TABLE t SET AUTHORIZATION [ROLE] user
17978                let mut auth_text = String::new();
17979                if self.match_texts(&["ROLE"]) {
17980                    auth_text.push_str("ROLE ");
17981                }
17982                let user = self.expect_identifier_or_keyword()?;
17983                auth_text.push_str(&user);
17984                Ok(AlterTableAction::SetAttribute {
17985                    attribute: format!("AUTHORIZATION {}", auth_text),
17986                })
17987            } else if self.match_identifier("PROPERTIES") {
17988                // Trino: ALTER TABLE t SET PROPERTIES x = 'y', ...
17989                let mut properties = Vec::new();
17990                loop {
17991                    // Parse property name (could be identifier or string literal)
17992                    let key = if self.check(TokenType::String) {
17993                        self.expect_string()?
17994                    } else {
17995                        self.expect_identifier_or_keyword()?
17996                    };
17997                    self.expect(TokenType::Eq)?;
17998                    // Parse value (could be DEFAULT or an expression)
17999                    let value = if self.match_token(TokenType::Default) {
18000                        // Use Var instead of Identifier so it won't be quoted
18001                        Expression::Var(Box::new(crate::expressions::Var {
18002                            this: "DEFAULT".to_string(),
18003                        }))
18004                    } else {
18005                        self.parse_expression()?
18006                    };
18007                    properties.push((key, value));
18008                    if !self.match_token(TokenType::Comma) {
18009                        break;
18010                    }
18011                }
18012                Ok(AlterTableAction::SetProperty { properties })
18013            } else if self.match_text_seq(&["TABLE", "PROPERTIES"]) {
18014                // Redshift: ALTER TABLE t SET TABLE PROPERTIES ('a' = '5', 'b' = 'c')
18015                self.expect(TokenType::LParen)?;
18016                let mut properties = Vec::new();
18017                loop {
18018                    if self.check(TokenType::RParen) {
18019                        break;
18020                    }
18021                    // Parse key (string literal)
18022                    let key = self.parse_primary()?;
18023                    self.expect(TokenType::Eq)?;
18024                    // Parse value (string literal)
18025                    let value = self.parse_primary()?;
18026                    properties.push((key, value));
18027                    if !self.match_token(TokenType::Comma) {
18028                        break;
18029                    }
18030                }
18031                self.expect(TokenType::RParen)?;
18032                Ok(AlterTableAction::SetTableProperties { properties })
18033            } else if self.match_text_seq(&["LOCATION"]) {
18034                // Redshift: ALTER TABLE t SET LOCATION 's3://bucket/folder/'
18035                let location = self.expect_string()?;
18036                Ok(AlterTableAction::SetLocation { location })
18037            } else if self.match_text_seq(&["FILE", "FORMAT"]) {
18038                // Redshift: ALTER TABLE t SET FILE FORMAT AVRO
18039                let format = self.expect_identifier_or_keyword()?;
18040                Ok(AlterTableAction::SetFileFormat { format })
18041            } else {
18042                // Snowflake: SET property=value, ...
18043                let mut properties = Vec::new();
18044                loop {
18045                    let key = self.expect_identifier_or_keyword()?;
18046                    self.expect(TokenType::Eq)?;
18047                    let value = self.parse_expression()?;
18048                    properties.push((key, value));
18049                    if !self.match_token(TokenType::Comma) {
18050                        break;
18051                    }
18052                }
18053                Ok(AlterTableAction::SetProperty { properties })
18054            }
18055        } else if self.match_keyword("UNSET") {
18056            // Snowflake: ALTER TABLE t UNSET property or UNSET TAG key
18057            if self.match_keyword("TAG") {
18058                // UNSET TAG key1, key2 (keys can be qualified like schema.tagname)
18059                let mut names = Vec::new();
18060                loop {
18061                    let mut name = self.expect_identifier_or_keyword()?;
18062                    while self.match_token(TokenType::Dot) {
18063                        let next = self.expect_identifier_or_keyword()?;
18064                        name = format!("{}.{}", name, next);
18065                    }
18066                    names.push(name);
18067                    if !self.match_token(TokenType::Comma) {
18068                        break;
18069                    }
18070                }
18071                Ok(AlterTableAction::UnsetTag { names })
18072            } else {
18073                // UNSET property1, property2
18074                let mut properties = Vec::new();
18075                loop {
18076                    let name = self.expect_identifier_or_keyword()?;
18077                    properties.push(name);
18078                    if !self.match_token(TokenType::Comma) {
18079                        break;
18080                    }
18081                }
18082                Ok(AlterTableAction::UnsetProperty { properties })
18083            }
18084        } else if self.match_keyword("CLUSTER") {
18085            // Snowflake: ALTER TABLE t CLUSTER BY (col1, col2 DESC)
18086            self.expect(TokenType::By)?;
18087            self.expect(TokenType::LParen)?;
18088            // Parse ordered expressions (can have ASC/DESC modifiers)
18089            let ordered = self.parse_order_by_list()?;
18090            // Convert Ordered to Expression (wrapping in Ordered if it has ordering)
18091            let expressions: Vec<Expression> = ordered
18092                .into_iter()
18093                .map(|o| Expression::Ordered(Box::new(o)))
18094                .collect();
18095            self.expect(TokenType::RParen)?;
18096            Ok(AlterTableAction::ClusterBy { expressions })
18097        } else if self.match_token(TokenType::Replace) {
18098            // ClickHouse: REPLACE PARTITION expr FROM table
18099            if self.match_token(TokenType::Partition) {
18100                let partition_expr = if self.match_text_seq(&["ALL"]) {
18101                    Expression::Identifier(Identifier::new("ALL".to_string()))
18102                } else if self.match_text_seq(&["ID"]) {
18103                    let id_val = self.parse_expression()?;
18104                    // Store as Raw to preserve "ID <value>" format
18105                    let id_str = match &id_val {
18106                        Expression::Literal(Literal::String(s)) => format!("ID '{}'", s),
18107                        _ => format!("ID {}", "?"),
18108                    };
18109                    Expression::Raw(Raw { sql: id_str })
18110                } else {
18111                    self.parse_expression()?
18112                };
18113                let source = if self.match_token(TokenType::From) {
18114                    let tref = self.parse_table_ref()?;
18115                    Some(Box::new(Expression::Table(Box::new(tref))))
18116                } else {
18117                    None
18118                };
18119                Ok(AlterTableAction::ReplacePartition {
18120                    partition: partition_expr,
18121                    source,
18122                })
18123            } else {
18124                Err(self.parse_error("Expected PARTITION after REPLACE in ALTER TABLE"))
18125            }
18126        } else if matches!(
18127            self.config.dialect,
18128            Some(crate::dialects::DialectType::ClickHouse)
18129        ) {
18130            // ClickHouse-specific ALTER TABLE mutations: UPDATE, DELETE, DETACH, ATTACH,
18131            // FREEZE, UNFREEZE, MATERIALIZE, CLEAR, COMMENT COLUMN, MODIFY ORDER BY,
18132            // MOVE PARTITION, FETCH PARTITION, ADD INDEX, DROP INDEX, CLEAR INDEX
18133            // For ClickHouse, consume any unrecognized ALTER TABLE action as Raw
18134            // (covers UPDATE, DELETE, DETACH, ATTACH, FREEZE, MOVE, FETCH, etc.)
18135            {
18136                let keyword = self.advance().text.clone();
18137                let mut tokens: Vec<(String, TokenType)> = vec![(keyword, TokenType::Var)];
18138                let mut paren_depth = 0i32;
18139                while !self.is_at_end() && !self.check(TokenType::Semicolon) {
18140                    // Stop at comma only when at top-level (not inside parens) — it separates ALTER actions
18141                    if self.check(TokenType::Comma) && paren_depth == 0 {
18142                        break;
18143                    }
18144                    let token = self.advance();
18145                    if token.token_type == TokenType::LParen {
18146                        paren_depth += 1;
18147                    }
18148                    if token.token_type == TokenType::RParen {
18149                        paren_depth -= 1;
18150                    }
18151                    let text = if token.token_type == TokenType::QuotedIdentifier {
18152                        format!("\"{}\"", token.text)
18153                    } else if token.token_type == TokenType::String {
18154                        format!("'{}'", token.text)
18155                    } else {
18156                        token.text.clone()
18157                    };
18158                    tokens.push((text, token.token_type));
18159                }
18160                Ok(AlterTableAction::Raw {
18161                    sql: self.join_command_tokens(tokens),
18162                })
18163            }
18164        } else {
18165            Err(self.parse_error(format!(
18166                "Expected ADD, DROP, RENAME, ALTER, SET, UNSET, SWAP, CLUSTER, or REPLACE in ALTER TABLE, got {:?}",
18167                self.peek().token_type
18168            )))
18169        }
18170    }
18171
18172    /// Parse TSQL SYSTEM_VERSIONING option in ALTER TABLE SET (...)
18173    /// Handles: SYSTEM_VERSIONING=OFF, SYSTEM_VERSIONING=ON, SYSTEM_VERSIONING=ON(HISTORY_TABLE=..., ...)
18174    fn parse_system_versioning_option(&mut self) -> Result<Expression> {
18175        self.skip(); // consume SYSTEM_VERSIONING
18176        self.expect(TokenType::Eq)?;
18177
18178        let mut prop = WithSystemVersioningProperty {
18179            on: None,
18180            this: None,
18181            data_consistency: None,
18182            retention_period: None,
18183            with_: None,
18184        };
18185
18186        if self.match_identifier("OFF") {
18187            // SYSTEM_VERSIONING=OFF
18188            // on is None => generates OFF
18189            return Ok(Expression::WithSystemVersioningProperty(Box::new(prop)));
18190        }
18191
18192        // SYSTEM_VERSIONING=ON or SYSTEM_VERSIONING=ON(...)
18193        if self.match_token(TokenType::On) || self.match_identifier("ON") {
18194            prop.on = Some(Box::new(Expression::Boolean(BooleanLiteral {
18195                value: true,
18196            })));
18197        }
18198
18199        if self.match_token(TokenType::LParen) {
18200            // Parse options inside ON(...)
18201            loop {
18202                if self.check(TokenType::RParen) {
18203                    break;
18204                }
18205                if self.match_identifier("HISTORY_TABLE") {
18206                    self.expect(TokenType::Eq)?;
18207                    let table = self.parse_table_ref()?;
18208                    prop.this = Some(Box::new(Expression::Table(Box::new(table))));
18209                } else if self.match_identifier("DATA_CONSISTENCY_CHECK") {
18210                    self.expect(TokenType::Eq)?;
18211                    let val = self.expect_identifier_or_keyword()?;
18212                    prop.data_consistency = Some(Box::new(Expression::Identifier(
18213                        Identifier::new(val.to_ascii_uppercase()),
18214                    )));
18215                } else if self.match_identifier("HISTORY_RETENTION_PERIOD") {
18216                    self.expect(TokenType::Eq)?;
18217                    if let Some(rp) = self.parse_retention_period()? {
18218                        prop.retention_period = Some(Box::new(rp));
18219                    }
18220                } else {
18221                    // Skip unknown options
18222                    self.skip();
18223                }
18224                if !self.match_token(TokenType::Comma) {
18225                    break;
18226                }
18227            }
18228            self.expect(TokenType::RParen)?;
18229        }
18230
18231        Ok(Expression::WithSystemVersioningProperty(Box::new(prop)))
18232    }
18233
18234    /// Parse TSQL DATA_DELETION option in ALTER TABLE SET (...)
18235    /// Handles: DATA_DELETION=ON, DATA_DELETION=OFF, DATA_DELETION=ON(FILTER_COLUMN=..., RETENTION_PERIOD=...)
18236    fn parse_data_deletion_option(&mut self) -> Result<Expression> {
18237        self.skip(); // consume DATA_DELETION
18238        self.expect(TokenType::Eq)?;
18239
18240        let on = if self.match_identifier("ON") || self.match_token(TokenType::On) {
18241            true
18242        } else if self.match_identifier("OFF") {
18243            false
18244        } else {
18245            false
18246        };
18247
18248        let on_expr = Box::new(Expression::Boolean(BooleanLiteral { value: on }));
18249        let mut filter_column = None;
18250        let mut retention_period = None;
18251
18252        if self.match_token(TokenType::LParen) {
18253            loop {
18254                if self.check(TokenType::RParen) {
18255                    break;
18256                }
18257                if self.match_identifier("FILTER_COLUMN") {
18258                    self.expect(TokenType::Eq)?;
18259                    let col = self.expect_identifier_or_keyword()?;
18260                    filter_column = Some(Box::new(Expression::boxed_column(Column {
18261                        name: Identifier::new(col),
18262                        table: None,
18263                        join_mark: false,
18264                        trailing_comments: Vec::new(),
18265                        span: None,
18266                        inferred_type: None,
18267                    })));
18268                } else if self.match_identifier("RETENTION_PERIOD") {
18269                    self.expect(TokenType::Eq)?;
18270                    if let Some(rp) = self.parse_retention_period()? {
18271                        retention_period = Some(Box::new(rp));
18272                    }
18273                } else {
18274                    self.skip();
18275                }
18276                if !self.match_token(TokenType::Comma) {
18277                    break;
18278                }
18279            }
18280            self.expect(TokenType::RParen)?;
18281        }
18282
18283        Ok(Expression::DataDeletionProperty(Box::new(
18284            DataDeletionProperty {
18285                on: on_expr,
18286                filter_column,
18287                retention_period,
18288            },
18289        )))
18290    }
18291
18292    /// Parse ALTER COLUMN action
18293    fn parse_alter_column_action(&mut self) -> Result<AlterColumnAction> {
18294        if self.match_token(TokenType::Set) {
18295            if self.match_keywords(&[TokenType::Not, TokenType::Null]) {
18296                Ok(AlterColumnAction::SetNotNull)
18297            } else if self.match_token(TokenType::Default) {
18298                let expr = self.parse_primary()?;
18299                Ok(AlterColumnAction::SetDefault(expr))
18300            } else if self.match_identifier("DATA") {
18301                // SET DATA TYPE
18302                // TYPE can be a keyword token or identifier
18303                let _ = self.match_token(TokenType::Type) || self.match_identifier("TYPE");
18304                let data_type = self.parse_data_type()?;
18305                // Optional COLLATE
18306                let collate = if self.match_token(TokenType::Collate) {
18307                    Some(self.expect_identifier_or_keyword()?)
18308                } else {
18309                    None
18310                };
18311                // Optional USING expression
18312                let using = if self.match_token(TokenType::Using) {
18313                    Some(self.parse_expression()?)
18314                } else {
18315                    None
18316                };
18317                Ok(AlterColumnAction::SetDataType {
18318                    data_type,
18319                    using,
18320                    collate,
18321                })
18322            } else if self.match_identifier("VISIBLE") {
18323                Ok(AlterColumnAction::SetVisible)
18324            } else if self.match_identifier("INVISIBLE") {
18325                Ok(AlterColumnAction::SetInvisible)
18326            } else {
18327                Err(self.parse_error("Expected NOT NULL, DEFAULT, VISIBLE, or INVISIBLE after SET"))
18328            }
18329        } else if self.match_token(TokenType::Drop) {
18330            if self.match_keywords(&[TokenType::Not, TokenType::Null]) {
18331                Ok(AlterColumnAction::DropNotNull)
18332            } else if self.match_token(TokenType::Default) {
18333                Ok(AlterColumnAction::DropDefault)
18334            } else {
18335                Err(self.parse_error("Expected NOT NULL or DEFAULT after DROP"))
18336            }
18337        } else if self.match_token(TokenType::Comment) {
18338            // ALTER COLUMN col COMMENT 'comment'
18339            let comment = self.expect_string()?;
18340            Ok(AlterColumnAction::Comment(comment))
18341        } else if self.match_token(TokenType::Type)
18342            || self.match_identifier("TYPE")
18343            || self.is_identifier_token()
18344        {
18345            // TYPE data_type or just data_type (PostgreSQL/Redshift: ALTER COLUMN col TYPE datatype)
18346            let data_type = self.parse_data_type()?;
18347            // Optional COLLATE
18348            let collate = if self.match_token(TokenType::Collate) {
18349                Some(self.expect_identifier_or_keyword()?)
18350            } else {
18351                None
18352            };
18353            // Optional USING expression
18354            let using = if self.match_token(TokenType::Using) {
18355                Some(self.parse_expression()?)
18356            } else {
18357                None
18358            };
18359            Ok(AlterColumnAction::SetDataType {
18360                data_type,
18361                using,
18362                collate,
18363            })
18364        } else {
18365            Err(self.parse_error("Expected SET, DROP, or TYPE in ALTER COLUMN"))
18366        }
18367    }
18368
18369    /// Parse TRUNCATE statement
18370    fn parse_truncate(&mut self) -> Result<Expression> {
18371        self.expect(TokenType::Truncate)?;
18372
18373        // ClickHouse: TRUNCATE ALL TABLES FROM [IF EXISTS] db
18374        if matches!(
18375            self.config.dialect,
18376            Some(crate::dialects::DialectType::ClickHouse)
18377        ) && self.check_identifier("ALL")
18378            && self.current + 1 < self.tokens.len()
18379            && self.tokens[self.current + 1]
18380                .text
18381                .eq_ignore_ascii_case("TABLES")
18382        {
18383            // Consume remaining tokens as Command
18384            let mut parts = vec!["TRUNCATE".to_string()];
18385            while !self.is_at_end() && !self.check(TokenType::Semicolon) {
18386                let token = self.advance();
18387                if token.token_type == TokenType::String {
18388                    parts.push(format!("'{}'", token.text));
18389                } else {
18390                    parts.push(token.text.clone());
18391                }
18392            }
18393            return Ok(Expression::Command(Box::new(crate::expressions::Command {
18394                this: parts.join(" "),
18395            })));
18396        }
18397
18398        let target = if self.match_token(TokenType::Database) {
18399            TruncateTarget::Database
18400        } else {
18401            // ClickHouse: TRUNCATE TEMPORARY TABLE t
18402            self.match_token(TokenType::Temporary);
18403            self.match_token(TokenType::Table); // optional TABLE keyword
18404            TruncateTarget::Table
18405        };
18406
18407        // Parse optional IF EXISTS
18408        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
18409
18410        // Parse first table with optional ONLY modifier
18411        let has_only = self.match_token(TokenType::Only);
18412        let mut table = self.parse_table_ref()?;
18413        if has_only {
18414            table.only = true;
18415        }
18416
18417        // Check for * suffix on table name (PostgreSQL: inherit children)
18418        let first_star = self.match_token(TokenType::Star);
18419
18420        // TSQL: WITH (PARTITIONS(1, 2 TO 5, 10 TO 20, 84))
18421        if self.check(TokenType::With) && self.check_next(TokenType::LParen) {
18422            if let Some(hint_expr) = self.parse_truncate_table_hints()? {
18423                match hint_expr {
18424                    Expression::Tuple(tuple) => {
18425                        table.hints = tuple.expressions;
18426                    }
18427                    other => {
18428                        table.hints = vec![other];
18429                    }
18430                }
18431            }
18432        }
18433
18434        // ClickHouse: ON CLUSTER clause
18435        let on_cluster = self.parse_on_cluster_clause()?;
18436
18437        // Parse additional tables for multi-table TRUNCATE
18438        let mut extra_tables = Vec::new();
18439        if first_star {
18440            // The first table has a * suffix, so create an entry for it
18441            extra_tables.push(TruncateTableEntry {
18442                table: table.clone(),
18443                star: true,
18444            });
18445        }
18446        while self.match_token(TokenType::Comma) {
18447            let extra_only = self.match_token(TokenType::Only);
18448            let mut extra_table = self.parse_table_ref()?;
18449            if extra_only {
18450                extra_table.only = true;
18451            }
18452            let extra_star = self.match_token(TokenType::Star);
18453            extra_tables.push(TruncateTableEntry {
18454                table: extra_table,
18455                star: extra_star,
18456            });
18457        }
18458
18459        // Parse RESTART IDENTITY / CONTINUE IDENTITY
18460        // RESTART is TokenType::Restart keyword, IDENTITY is TokenType::Identity keyword
18461        let identity = if self.match_token(TokenType::Restart) {
18462            self.match_token(TokenType::Identity);
18463            Some(TruncateIdentity::Restart)
18464        } else if self.match_identifier("CONTINUE") {
18465            self.match_token(TokenType::Identity);
18466            Some(TruncateIdentity::Continue)
18467        } else {
18468            None
18469        };
18470
18471        // Parse CASCADE or RESTRICT
18472        // CASCADE is TokenType::Cascade keyword, RESTRICT is TokenType::Restrict keyword
18473        let cascade = self.match_token(TokenType::Cascade);
18474        let restrict = if !cascade {
18475            self.match_token(TokenType::Restrict)
18476        } else {
18477            false
18478        };
18479
18480        // Parse Hive PARTITION clause: PARTITION(key = value, ...)
18481        // parse_partition consumes the PARTITION keyword itself
18482        let partition = self.parse_partition()?;
18483
18484        // ClickHouse: TRUNCATE TABLE t SETTINGS key=value, ...
18485        if matches!(
18486            self.config.dialect,
18487            Some(crate::dialects::DialectType::ClickHouse)
18488        ) && self.match_token(TokenType::Settings)
18489        {
18490            // Consume settings expressions (they're not stored in the AST for TRUNCATE)
18491            loop {
18492                let _ = self.parse_expression()?;
18493                if !self.match_token(TokenType::Comma) {
18494                    break;
18495                }
18496            }
18497        }
18498
18499        Ok(Expression::Truncate(Box::new(Truncate {
18500            target,
18501            if_exists,
18502            table,
18503            on_cluster,
18504            cascade,
18505            extra_tables,
18506            identity,
18507            restrict,
18508            partition: partition.map(Box::new),
18509        })))
18510    }
18511
18512    /// Parse VALUES table constructor: VALUES (1, 'a'), (2, 'b')
18513    fn parse_values(&mut self) -> Result<Expression> {
18514        self.expect(TokenType::Values)?;
18515
18516        let mut expressions = Vec::new();
18517
18518        // Handle bare VALUES without parentheses: VALUES 1, 2, 3 -> VALUES (1), (2), (3)
18519        if !self.check(TokenType::LParen) {
18520            loop {
18521                let val = self.parse_expression()?;
18522                expressions.push(Tuple {
18523                    expressions: vec![val],
18524                });
18525                if !self.match_token(TokenType::Comma) {
18526                    break;
18527                }
18528            }
18529        } else {
18530            loop {
18531                self.expect(TokenType::LParen)?;
18532                // Parse VALUES tuple elements with optional AS aliases (Hive syntax)
18533                let row_values = self.parse_values_expression_list()?;
18534                self.expect(TokenType::RParen)?;
18535
18536                expressions.push(Tuple {
18537                    expressions: row_values,
18538                });
18539
18540                if !self.match_token(TokenType::Comma) {
18541                    break;
18542                }
18543                // ClickHouse: allow trailing comma after last tuple
18544                if matches!(
18545                    self.config.dialect,
18546                    Some(crate::dialects::DialectType::ClickHouse)
18547                ) && !self.check(TokenType::LParen)
18548                {
18549                    break;
18550                }
18551            }
18552        }
18553
18554        // Check for alias: VALUES (1, 2) AS new_data or VALUES (1, 2) new_data
18555        let (alias, column_aliases) = if self.match_token(TokenType::As) {
18556            let alias_name = self.expect_identifier()?;
18557            let alias = Some(Identifier::new(alias_name));
18558
18559            // Check for column aliases: AS new_data(a, b)
18560            let col_aliases = if self.match_token(TokenType::LParen) {
18561                let aliases = self.parse_identifier_list()?;
18562                self.expect(TokenType::RParen)?;
18563                aliases
18564            } else {
18565                Vec::new()
18566            };
18567            (alias, col_aliases)
18568        } else if self.check(TokenType::Var) && !self.check_keyword() {
18569            // Implicit alias: VALUES (0) foo(bar)
18570            let alias_name = self.advance().text.clone();
18571            let alias = Some(Identifier::new(alias_name));
18572            let col_aliases = if self.match_token(TokenType::LParen) {
18573                let aliases = self.parse_identifier_list()?;
18574                self.expect(TokenType::RParen)?;
18575                aliases
18576            } else {
18577                Vec::new()
18578            };
18579            (alias, col_aliases)
18580        } else {
18581            (None, Vec::new())
18582        };
18583
18584        // VALUES can be followed by set operations (UNION, etc.)
18585        let values_expr = Expression::Values(Box::new(Values {
18586            expressions,
18587            alias,
18588            column_aliases,
18589        }));
18590
18591        // Check for set operations after VALUES
18592        self.parse_set_operation(values_expr)
18593    }
18594
18595    /// Parse USE statement: USE db, USE DATABASE x, USE SCHEMA x.y, USE ROLE x, etc.
18596    fn parse_use(&mut self) -> Result<Expression> {
18597        self.expect(TokenType::Use)?;
18598
18599        // Check for Snowflake: USE SECONDARY ROLES ALL|NONE|role1, role2, ...
18600        if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("SECONDARY") {
18601            self.skip(); // consume SECONDARY
18602                            // Check for ROLES
18603            if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("ROLES") {
18604                self.skip(); // consume ROLES
18605                                // Parse ALL, NONE, or comma-separated role list
18606                let mut roles = Vec::new();
18607                loop {
18608                    if self.check(TokenType::Var)
18609                        || self.check(TokenType::All)
18610                        || self.check(TokenType::Identifier)
18611                    {
18612                        let role = self.advance().text.clone();
18613                        roles.push(role);
18614                        if !self.match_token(TokenType::Comma) {
18615                            break;
18616                        }
18617                    } else {
18618                        break;
18619                    }
18620                }
18621                let name = if roles.is_empty() {
18622                    "ALL".to_string()
18623                } else {
18624                    roles.join(", ")
18625                };
18626                return Ok(Expression::Use(Box::new(Use {
18627                    kind: Some(UseKind::SecondaryRoles),
18628                    this: Identifier::new(name),
18629                })));
18630            }
18631        }
18632
18633        // Check for kind: DATABASE, SCHEMA, ROLE, WAREHOUSE, CATALOG
18634        // Note: ROLE and CATALOG are not keywords, so we check the text
18635        let kind = if self.match_token(TokenType::Database) {
18636            Some(UseKind::Database)
18637        } else if self.match_token(TokenType::Schema) {
18638            Some(UseKind::Schema)
18639        } else if self.match_token(TokenType::Warehouse) {
18640            Some(UseKind::Warehouse)
18641        } else if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("ROLE") {
18642            self.skip();
18643            Some(UseKind::Role)
18644        } else if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("CATALOG") {
18645            self.skip();
18646            Some(UseKind::Catalog)
18647        } else {
18648            None
18649        };
18650
18651        // Parse the name (can be qualified like x.y)
18652        // Use expect_identifier_or_keyword_with_quoted because names like "default", "system" are valid
18653        let mut ident = self.expect_identifier_or_keyword_with_quoted()?;
18654
18655        // Handle qualified names like schema.table for USE SCHEMA x.y
18656        if self.match_token(TokenType::Dot) {
18657            let second_part = self.expect_identifier_or_keyword_with_quoted()?;
18658            ident.name = format!("{}.{}", ident.name, second_part.name);
18659        }
18660
18661        Ok(Expression::Use(Box::new(Use { kind, this: ident })))
18662    }
18663
18664    /// Parse EXPORT DATA statement (BigQuery)
18665    /// EXPORT DATA [WITH CONNECTION connection] OPTIONS (...) AS SELECT ...
18666    fn parse_export_data(&mut self) -> Result<Expression> {
18667        self.skip(); // consume EXPORT
18668
18669        // Expect DATA
18670        if !self.match_identifier("DATA") {
18671            return Err(self.parse_error("Expected DATA after EXPORT"));
18672        }
18673
18674        // Optional: WITH CONNECTION connection
18675        let connection = if self.match_text_seq(&["WITH", "CONNECTION"]) {
18676            // Parse connection identifier (can be qualified: project.location.connection)
18677            let first = self.expect_identifier()?;
18678            let connection_name = if self.match_token(TokenType::Dot) {
18679                let second = self.expect_identifier()?;
18680                if self.match_token(TokenType::Dot) {
18681                    let third = self.expect_identifier()?;
18682                    format!("{}.{}.{}", first, second, third)
18683                } else {
18684                    format!("{}.{}", first, second)
18685                }
18686            } else {
18687                first
18688            };
18689            Some(Box::new(Expression::Identifier(Identifier::new(
18690                connection_name,
18691            ))))
18692        } else {
18693            None
18694        };
18695
18696        // Expect OPTIONS (...)
18697        let options = if self.match_identifier("OPTIONS") {
18698            self.parse_options_list()?
18699        } else {
18700            Vec::new()
18701        };
18702
18703        // Expect AS
18704        self.expect(TokenType::As)?;
18705
18706        // Parse the SELECT query
18707        let query = self.parse_statement()?;
18708
18709        Ok(Expression::Export(Box::new(Export {
18710            this: Box::new(query),
18711            connection,
18712            options,
18713        })))
18714    }
18715
18716    /// Parse CACHE TABLE statement (Spark)
18717    /// CACHE [LAZY] TABLE name [OPTIONS(...)] [AS query]
18718    fn parse_cache(&mut self) -> Result<Expression> {
18719        self.expect(TokenType::Cache)?;
18720
18721        // Check for LAZY keyword
18722        let lazy = self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("LAZY");
18723        if lazy {
18724            self.skip();
18725        }
18726
18727        self.expect(TokenType::Table)?;
18728        let table = Identifier::new(self.expect_identifier()?);
18729
18730        // Check for OPTIONS clause
18731        let options = if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("OPTIONS")
18732        {
18733            self.skip();
18734            self.expect(TokenType::LParen)?;
18735            let mut opts = Vec::new();
18736            loop {
18737                // Parse key = value pairs (key can be string literal or identifier)
18738                let key = if self.check(TokenType::NationalString) {
18739                    let token = self.advance();
18740                    Expression::Literal(Literal::NationalString(token.text))
18741                } else if self.check(TokenType::String) {
18742                    let token = self.advance();
18743                    Expression::Literal(Literal::String(token.text))
18744                } else {
18745                    Expression::Identifier(Identifier::new(self.expect_identifier()?))
18746                };
18747                // Eq is optional - Spark allows space-separated key value pairs
18748                // e.g., OPTIONS ('storageLevel' 'DISK_ONLY') or OPTIONS ('key' = 'value')
18749                let _ = self.match_token(TokenType::Eq);
18750                let value = self.parse_expression()?;
18751                opts.push((key, value));
18752                if !self.match_token(TokenType::Comma) {
18753                    break;
18754                }
18755            }
18756            self.expect(TokenType::RParen)?;
18757            opts
18758        } else {
18759            Vec::new()
18760        };
18761
18762        // Check for AS clause or implicit query (SELECT without AS in Spark)
18763        let query = if self.match_token(TokenType::As) {
18764            Some(self.parse_statement()?)
18765        } else if self.check(TokenType::Select) || self.check(TokenType::With) {
18766            // Spark allows SELECT without AS keyword after CACHE TABLE
18767            Some(self.parse_statement()?)
18768        } else {
18769            None
18770        };
18771
18772        Ok(Expression::Cache(Box::new(Cache {
18773            table,
18774            lazy,
18775            options,
18776            query,
18777        })))
18778    }
18779
18780    /// Parse UNCACHE TABLE statement (Spark)
18781    /// UNCACHE TABLE [IF EXISTS] name
18782    fn parse_uncache(&mut self) -> Result<Expression> {
18783        self.expect(TokenType::Uncache)?;
18784        self.expect(TokenType::Table)?;
18785
18786        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
18787        let table = Identifier::new(self.expect_identifier()?);
18788
18789        Ok(Expression::Uncache(Box::new(Uncache { table, if_exists })))
18790    }
18791
18792    /// Parse LOAD DATA statement (Hive)
18793    /// LOAD DATA [LOCAL] INPATH 'path' [OVERWRITE] INTO TABLE table_name
18794    /// [PARTITION (col=val, ...)] [INPUTFORMAT 'format'] [SERDE 'serde']
18795    fn parse_load_data(&mut self) -> Result<Expression> {
18796        self.expect(TokenType::Load)?;
18797
18798        // Expect DATA keyword
18799        let data_token = self.advance();
18800        if !data_token.text.eq_ignore_ascii_case("DATA") {
18801            return Err(self.parse_error("Expected DATA after LOAD"));
18802        }
18803
18804        // Check for LOCAL keyword
18805        let local = self.match_token(TokenType::Local);
18806
18807        // Expect INPATH
18808        self.expect(TokenType::Inpath)?;
18809
18810        // Parse the path (string literal)
18811        let inpath = if self.check(TokenType::String) {
18812            self.advance().text
18813        } else {
18814            return Err(self.parse_error("Expected string literal after INPATH"));
18815        };
18816
18817        // Check for OVERWRITE keyword
18818        let overwrite = self.match_token(TokenType::Overwrite);
18819
18820        // Expect INTO TABLE
18821        self.expect(TokenType::Into)?;
18822        self.expect(TokenType::Table)?;
18823
18824        // Parse table name (can be qualified)
18825        let table = Expression::Table(Box::new(self.parse_table_ref()?));
18826
18827        // Check for PARTITION clause
18828        let partition = if self.match_token(TokenType::Partition) {
18829            self.expect(TokenType::LParen)?;
18830            let mut partitions = Vec::new();
18831            loop {
18832                let col = Identifier::new(self.expect_identifier_or_keyword()?);
18833                self.expect(TokenType::Eq)?;
18834                let val = self.parse_expression()?;
18835                partitions.push((col, val));
18836                if !self.match_token(TokenType::Comma) {
18837                    break;
18838                }
18839            }
18840            self.expect(TokenType::RParen)?;
18841            partitions
18842        } else {
18843            Vec::new()
18844        };
18845
18846        // Check for INPUTFORMAT clause
18847        let input_format = if self.match_token(TokenType::InputFormat) {
18848            if self.check(TokenType::String) {
18849                Some(self.advance().text)
18850            } else {
18851                return Err(self.parse_error("Expected string literal after INPUTFORMAT"));
18852            }
18853        } else {
18854            None
18855        };
18856
18857        // Check for SERDE clause
18858        let serde = if self.match_token(TokenType::Serde) {
18859            if self.check(TokenType::String) {
18860                Some(self.advance().text)
18861            } else {
18862                return Err(self.parse_error("Expected string literal after SERDE"));
18863            }
18864        } else {
18865            None
18866        };
18867
18868        Ok(Expression::LoadData(Box::new(LoadData {
18869            local,
18870            inpath,
18871            overwrite,
18872            table,
18873            partition,
18874            input_format,
18875            serde,
18876        })))
18877    }
18878
18879    /// Parse PRAGMA statement (SQLite)
18880    /// PRAGMA [schema.]name [= value | (args...)]
18881    fn parse_pragma(&mut self) -> Result<Expression> {
18882        self.expect(TokenType::Pragma)?;
18883
18884        // Parse schema.name or just name
18885        let first_name = self.expect_identifier_or_keyword()?;
18886
18887        let (schema, name) = if self.match_token(TokenType::Dot) {
18888            // First name was schema
18889            let pragma_name = self.expect_identifier_or_keyword()?;
18890            (
18891                Some(Identifier::new(first_name)),
18892                Identifier::new(pragma_name),
18893            )
18894        } else {
18895            (None, Identifier::new(first_name))
18896        };
18897
18898        // Check for assignment or function call
18899        let (value, args) = if self.match_token(TokenType::Eq) {
18900            // PRAGMA name = value
18901            let val = self.parse_expression()?;
18902            (Some(val), Vec::new())
18903        } else if self.match_token(TokenType::LParen) {
18904            // PRAGMA name(args...)
18905            let mut arguments = Vec::new();
18906            if !self.check(TokenType::RParen) {
18907                loop {
18908                    arguments.push(self.parse_expression()?);
18909                    if !self.match_token(TokenType::Comma) {
18910                        break;
18911                    }
18912                }
18913            }
18914            self.expect(TokenType::RParen)?;
18915            (None, arguments)
18916        } else {
18917            (None, Vec::new())
18918        };
18919
18920        Ok(Expression::Pragma(Box::new(Pragma {
18921            schema,
18922            name,
18923            value,
18924            args,
18925        })))
18926    }
18927
18928    /// Parse ROLLBACK statement
18929    /// ROLLBACK [TO [SAVEPOINT] <name>]
18930    fn parse_rollback(&mut self) -> Result<Expression> {
18931        self.expect(TokenType::Rollback)?;
18932
18933        // Check for optional TRANSACTION, TRAN, or WORK keyword
18934        let has_transaction = self.match_token(TokenType::Transaction)
18935            || self.match_identifier("TRAN")
18936            || self.match_identifier("WORK");
18937
18938        // Check for TO SAVEPOINT (standard SQL) or transaction name (TSQL)
18939        let (savepoint, this) = if self.match_token(TokenType::To) {
18940            // Optional SAVEPOINT keyword
18941            self.match_token(TokenType::Savepoint);
18942            // Savepoint name
18943            if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
18944                let name = self.advance().text;
18945                (
18946                    Some(Box::new(Expression::Identifier(Identifier::new(name)))),
18947                    None,
18948                )
18949            } else {
18950                (None, None)
18951            }
18952        } else if has_transaction
18953            && (self.is_identifier_token() || self.is_safe_keyword_as_identifier())
18954        {
18955            // TSQL: ROLLBACK TRANSACTION transaction_name
18956            let name = self.advance().text;
18957            (
18958                None,
18959                Some(Box::new(Expression::Identifier(Identifier::new(name)))),
18960            )
18961        } else if has_transaction {
18962            // Just ROLLBACK TRANSACTION - store marker
18963            (
18964                None,
18965                Some(Box::new(Expression::Identifier(Identifier::new(
18966                    "TRANSACTION".to_string(),
18967                )))),
18968            )
18969        } else {
18970            (None, None)
18971        };
18972
18973        Ok(Expression::Rollback(Box::new(Rollback { savepoint, this })))
18974    }
18975
18976    /// Parse COMMIT statement
18977    /// COMMIT [TRANSACTION|TRAN|WORK] [transaction_name] [WITH (DELAYED_DURABILITY = ON|OFF)] [AND [NO] CHAIN]
18978    fn parse_commit(&mut self) -> Result<Expression> {
18979        self.expect(TokenType::Commit)?;
18980
18981        // Check for optional TRANSACTION, TRAN, or WORK keyword
18982        let has_transaction = self.match_token(TokenType::Transaction)
18983            || self.match_identifier("TRAN")
18984            || self.match_identifier("WORK");
18985
18986        // Parse optional transaction name (TSQL)
18987        let this = if has_transaction
18988            && (self.is_identifier_token() || self.is_safe_keyword_as_identifier())
18989            && !self.check(TokenType::With)
18990            && !self.check(TokenType::And)
18991        {
18992            let name = self.advance().text;
18993            Some(Box::new(Expression::Identifier(Identifier::new(name))))
18994        } else if has_transaction {
18995            // Store marker that TRANSACTION keyword was present
18996            Some(Box::new(Expression::Identifier(Identifier::new(
18997                "TRANSACTION".to_string(),
18998            ))))
18999        } else {
19000            None
19001        };
19002
19003        // Parse WITH (DELAYED_DURABILITY = ON|OFF) for TSQL
19004        let durability = if self.match_token(TokenType::With) && self.match_token(TokenType::LParen)
19005        {
19006            // Check for DELAYED_DURABILITY
19007            if self.match_identifier("DELAYED_DURABILITY") && self.match_token(TokenType::Eq) {
19008                // ON is a keyword (TokenType::On), OFF is an identifier
19009                let on = self.match_token(TokenType::On) || self.match_identifier("ON");
19010                if !on {
19011                    self.match_identifier("OFF");
19012                }
19013                self.expect(TokenType::RParen)?;
19014                Some(Box::new(Expression::Boolean(BooleanLiteral { value: on })))
19015            } else {
19016                // Skip to RParen
19017                while !self.check(TokenType::RParen) && !self.is_at_end() {
19018                    self.skip();
19019                }
19020                self.match_token(TokenType::RParen);
19021                None
19022            }
19023        } else {
19024            None
19025        };
19026
19027        // Parse AND [NO] CHAIN
19028        let chain = if self.match_token(TokenType::And) {
19029            let no_chain = self.match_token(TokenType::No);
19030            self.match_identifier("CHAIN");
19031            if no_chain {
19032                // AND NO CHAIN - explicit false
19033                Some(Box::new(Expression::Boolean(BooleanLiteral {
19034                    value: false,
19035                })))
19036            } else {
19037                // AND CHAIN - explicit true
19038                Some(Box::new(Expression::Boolean(BooleanLiteral {
19039                    value: true,
19040                })))
19041            }
19042        } else {
19043            None
19044        };
19045
19046        Ok(Expression::Commit(Box::new(Commit {
19047            chain,
19048            this,
19049            durability,
19050        })))
19051    }
19052
19053    /// Parse END statement (PostgreSQL alias for COMMIT)
19054    /// END [WORK|TRANSACTION] [AND [NO] CHAIN]
19055    fn parse_end_transaction(&mut self) -> Result<Expression> {
19056        self.expect(TokenType::End)?;
19057
19058        // Check for optional WORK or TRANSACTION keyword
19059        let _has_work = self.match_identifier("WORK") || self.match_token(TokenType::Transaction);
19060
19061        // Parse AND [NO] CHAIN
19062        let chain = if self.match_token(TokenType::And) {
19063            let no_chain = self.match_token(TokenType::No);
19064            self.match_identifier("CHAIN");
19065            if no_chain {
19066                // AND NO CHAIN - explicit false
19067                Some(Box::new(Expression::Boolean(BooleanLiteral {
19068                    value: false,
19069                })))
19070            } else {
19071                // AND CHAIN - explicit true
19072                Some(Box::new(Expression::Boolean(BooleanLiteral {
19073                    value: true,
19074                })))
19075            }
19076        } else {
19077            None
19078        };
19079
19080        // Return as COMMIT since END is an alias
19081        Ok(Expression::Commit(Box::new(Commit {
19082            chain,
19083            this: None,
19084            durability: None,
19085        })))
19086    }
19087
19088    /// Parse BEGIN/START TRANSACTION statement
19089    /// BEGIN [DEFERRED|IMMEDIATE|EXCLUSIVE] [TRANSACTION|TRAN|WORK] [transaction_name] [WITH MARK 'description']
19090    /// Also handles procedural BEGIN blocks (BigQuery, etc.): BEGIN statement_list END
19091    fn parse_transaction(&mut self) -> Result<Expression> {
19092        self.expect(TokenType::Begin)?;
19093
19094        // Check if this is a procedural BEGIN block rather than a transaction
19095        // If next token is not a transaction keyword and we have more tokens, it's a procedural block
19096        let is_transaction = self.is_at_end()
19097            || self.check(TokenType::Semicolon)
19098            || self.check(TokenType::Transaction)
19099            || self.check_identifier("TRAN")
19100            || self.check_identifier("WORK")
19101            || self.check_identifier("DEFERRED")
19102            || self.check_identifier("IMMEDIATE")
19103            || self.check_identifier("EXCLUSIVE");
19104
19105        if !is_transaction {
19106            // This is a procedural BEGIN block - parse as Command
19107            // Collect remaining tokens until end of statement
19108            return self
19109                .parse_command()?
19110                .ok_or_else(|| self.parse_error("Failed to parse BEGIN block"));
19111        }
19112
19113        // Check for transaction kind: DEFERRED, IMMEDIATE, EXCLUSIVE (SQLite)
19114        let kind = if self.match_identifier("DEFERRED")
19115            || self.match_identifier("IMMEDIATE")
19116            || self.match_identifier("EXCLUSIVE")
19117        {
19118            Some(self.previous().text.clone())
19119        } else {
19120            None
19121        };
19122
19123        // Check for TRANSACTION, TRAN, or WORK keyword
19124        let has_transaction_keyword = self.match_token(TokenType::Transaction)
19125            || self.match_identifier("TRAN")
19126            || self.match_identifier("WORK");
19127
19128        // Parse optional transaction name (TSQL style: BEGIN TRANSACTION trans_name)
19129        let trans_name = if has_transaction_keyword
19130            && (self.is_identifier_token() || self.is_safe_keyword_as_identifier())
19131            && !self.check(TokenType::With)
19132        {
19133            // Could be a transaction name or @variable
19134            let name = self.advance().text;
19135            Some(name)
19136        } else {
19137            None
19138        };
19139
19140        // Combine kind and trans_name into `this`
19141        let this = if let Some(name) = trans_name {
19142            Some(Box::new(Expression::Identifier(Identifier::new(name))))
19143        } else if let Some(k) = kind {
19144            Some(Box::new(Expression::Identifier(Identifier::new(k))))
19145        } else {
19146            None
19147        };
19148
19149        // Parse WITH MARK 'description' (TSQL)
19150        let mark = if self.match_token(TokenType::With) && self.match_identifier("MARK") {
19151            if self.check(TokenType::String) {
19152                let desc = self.advance().text;
19153                Some(Box::new(Expression::Literal(Literal::String(desc))))
19154            } else {
19155                Some(Box::new(Expression::Literal(Literal::String(
19156                    "".to_string(),
19157                ))))
19158            }
19159        } else if has_transaction_keyword {
19160            // Store "TRANSACTION" marker to preserve round-trip
19161            Some(Box::new(Expression::Identifier(Identifier::new(
19162                "TRANSACTION".to_string(),
19163            ))))
19164        } else {
19165            None
19166        };
19167
19168        // Parse any additional transaction modes (isolation levels, etc.)
19169        let mut mode_parts: Vec<String> = Vec::new();
19170        while self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
19171            let mut mode_tokens: Vec<String> = Vec::new();
19172            while (self.is_identifier_token() || self.is_safe_keyword_as_identifier())
19173                && !self.check(TokenType::Comma)
19174            {
19175                mode_tokens.push(self.advance().text);
19176            }
19177            if !mode_tokens.is_empty() {
19178                mode_parts.push(mode_tokens.join(" "));
19179            }
19180            if !self.match_token(TokenType::Comma) {
19181                break;
19182            }
19183        }
19184
19185        let modes = if !mode_parts.is_empty() {
19186            Some(Box::new(Expression::Identifier(Identifier::new(
19187                mode_parts.join(", "),
19188            ))))
19189        } else {
19190            None
19191        };
19192
19193        Ok(Expression::Transaction(Box::new(Transaction {
19194            this,
19195            modes,
19196            mark,
19197        })))
19198    }
19199
19200    /// Parse START TRANSACTION statement
19201    /// START TRANSACTION [READ ONLY | READ WRITE] [, ISOLATION LEVEL ...]
19202    fn parse_start_transaction(&mut self) -> Result<Expression> {
19203        self.expect(TokenType::Start)?;
19204
19205        // Expect TRANSACTION keyword
19206        self.expect(TokenType::Transaction)?;
19207
19208        // Parse any transaction modes (READ ONLY, READ WRITE, ISOLATION LEVEL, etc.)
19209        let mut mode_parts: Vec<String> = Vec::new();
19210        while self.is_identifier_token()
19211            || self.is_safe_keyword_as_identifier()
19212            || self.match_identifier("READ")
19213        {
19214            // If we matched READ, add it to tokens
19215            let read_matched = if self.previous().text.eq_ignore_ascii_case("READ") {
19216                true
19217            } else {
19218                false
19219            };
19220            let mut mode_tokens: Vec<String> = Vec::new();
19221            if read_matched {
19222                mode_tokens.push("READ".to_string());
19223            }
19224            while (self.is_identifier_token() || self.is_safe_keyword_as_identifier())
19225                && !self.check(TokenType::Comma)
19226            {
19227                mode_tokens.push(self.advance().text);
19228            }
19229            if !mode_tokens.is_empty() {
19230                mode_parts.push(mode_tokens.join(" "));
19231            }
19232            if !self.match_token(TokenType::Comma) {
19233                break;
19234            }
19235        }
19236
19237        let modes = if !mode_parts.is_empty() {
19238            Some(Box::new(Expression::Identifier(Identifier::new(
19239                mode_parts.join(", "),
19240            ))))
19241        } else {
19242            None
19243        };
19244
19245        Ok(Expression::Transaction(Box::new(Transaction {
19246            this: None, // START TRANSACTION doesn't have a kind like DEFERRED/IMMEDIATE
19247            modes,
19248            // Mark as START to differentiate from BEGIN
19249            mark: Some(Box::new(Expression::Identifier(Identifier::new(
19250                "START".to_string(),
19251            )))),
19252        })))
19253    }
19254
19255    /// Parse DESCRIBE statement
19256    /// DESCRIBE [EXTENDED|FORMATTED|ANALYZE] <table_or_query>
19257    /// Also handles EXPLAIN (parsed as Describe)
19258    fn parse_describe(&mut self) -> Result<Expression> {
19259        // Accept DESCRIBE, DESC, and EXPLAIN (Var token)
19260        // Capture leading comments from the first token
19261        let leading_comments = if self.check(TokenType::Describe) {
19262            let token = self.advance();
19263            token.comments
19264        } else if self.check(TokenType::Desc) {
19265            let token = self.advance();
19266            token.comments
19267        } else if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("EXPLAIN") {
19268            let token = self.advance(); // consume EXPLAIN
19269            token.comments
19270        } else {
19271            return Err(self.parse_error("Expected DESCRIBE, DESC, or EXPLAIN"));
19272        };
19273
19274        // Check for EXTENDED or FORMATTED keywords
19275        let extended = self.match_identifier("EXTENDED");
19276        let formatted = if !extended {
19277            self.match_identifier("FORMATTED")
19278        } else {
19279            false
19280        };
19281
19282        // Check for style keywords like ANALYZE, HISTORY
19283        // ClickHouse: EXPLAIN SYNTAX/AST/PLAN/PIPELINE/ESTIMATE/TABLE OVERRIDE/CURRENT TRANSACTION
19284        // For HISTORY, we need to look ahead to ensure it's not part of a schema-qualified
19285        // table name like "history.tbl". If the next token is a Dot, "history" is a schema name.
19286        let style = if !extended && !formatted && self.match_identifier("ANALYZE") {
19287            Some("ANALYZE".to_string())
19288        } else if !extended
19289            && !formatted
19290            && matches!(
19291                self.config.dialect,
19292                Some(crate::dialects::DialectType::ClickHouse)
19293            )
19294        {
19295            // ClickHouse EXPLAIN styles
19296            let text_upper = if !self.is_at_end() {
19297                self.peek().text.to_ascii_uppercase()
19298            } else {
19299                String::new()
19300            };
19301            match text_upper.as_str() {
19302                "SYNTAX" | "AST" | "PLAN" | "PIPELINE" | "ESTIMATE" | "QUERY" | "CURRENT" => {
19303                    self.skip();
19304                    let mut style_str = text_upper;
19305                    // Handle multi-word: TABLE OVERRIDE, CURRENT TRANSACTION, QUERY TREE
19306                    if style_str == "CURRENT" && self.check_identifier("TRANSACTION") {
19307                        style_str.push_str(" TRANSACTION");
19308                        self.skip();
19309                    }
19310                    if style_str == "QUERY" && self.check_identifier("TREE") {
19311                        style_str.push_str(" TREE");
19312                        self.skip();
19313                    }
19314                    Some(style_str)
19315                }
19316                _ if self.check(TokenType::Table) => {
19317                    // EXPLAIN TABLE OVERRIDE
19318                    self.skip(); // consume TABLE
19319                    if self.check_identifier("OVERRIDE") {
19320                        self.skip();
19321                        Some("TABLE OVERRIDE".to_string())
19322                    } else {
19323                        // Not TABLE OVERRIDE, backtrack
19324                        self.current -= 1;
19325                        None
19326                    }
19327                }
19328                _ => None,
19329            }
19330        } else if !extended
19331            && !formatted
19332            && (self.check(TokenType::Identifier)
19333                || self.check(TokenType::Var)
19334                || self.check(TokenType::QuotedIdentifier))
19335            && self.peek().text.eq_ignore_ascii_case("HISTORY")
19336            && self.peek_nth(1).map(|t| t.token_type) != Some(TokenType::Dot)
19337        {
19338            self.skip(); // consume HISTORY
19339            Some("HISTORY".to_string())
19340        } else {
19341            None
19342        };
19343
19344        // Check for object kind like SEMANTIC VIEW, TABLE, INPUT, OUTPUT, etc.
19345        let kind = if self.match_identifier("SEMANTIC") {
19346            if self.match_token(TokenType::View) {
19347                Some("SEMANTIC VIEW".to_string())
19348            } else {
19349                Some("SEMANTIC".to_string())
19350            }
19351        } else if self.match_token(TokenType::Table) {
19352            Some("TABLE".to_string())
19353        } else if self.match_token(TokenType::View) {
19354            Some("VIEW".to_string())
19355        } else if self.match_identifier("DATABASE") {
19356            Some("DATABASE".to_string())
19357        } else if self.match_identifier("SCHEMA") {
19358            Some("SCHEMA".to_string())
19359        } else if self.match_token(TokenType::Input) {
19360            Some("INPUT".to_string())
19361        } else if self.match_token(TokenType::Output) {
19362            Some("OUTPUT".to_string())
19363        } else {
19364            None
19365        };
19366
19367        // ClickHouse: parse EXPLAIN settings before the target statement
19368        // e.g., EXPLAIN actions=1, description=0 SELECT ...
19369        // e.g., EXPLAIN PLAN actions=1 SELECT ...
19370        let mut properties = Vec::new();
19371        if matches!(
19372            self.config.dialect,
19373            Some(crate::dialects::DialectType::ClickHouse)
19374        ) {
19375            while !self.is_at_end() && !self.check(TokenType::Semicolon) {
19376                // Look for key=value pairs before a statement keyword
19377                if (self.is_identifier_token()
19378                    || self.is_safe_keyword_as_identifier()
19379                    || self.check(TokenType::Type))
19380                    && self.current + 1 < self.tokens.len()
19381                    && self.tokens[self.current + 1].token_type == TokenType::Eq
19382                {
19383                    let name = self.advance().text.to_lowercase();
19384                    self.skip(); // consume =
19385                    let value = self.advance().text.clone();
19386                    properties.push((name, value));
19387                    self.match_token(TokenType::Comma); // optional comma between settings
19388                } else {
19389                    break;
19390                }
19391            }
19392        }
19393
19394        // Parse target - could be a table name or a SELECT/INSERT/other statement
19395        // ClickHouse: EXPLAIN/DESC can precede any statement or subquery
19396        let target = if self.check(TokenType::Select) || self.check(TokenType::With) {
19397            self.parse_statement()?
19398        } else if self.check(TokenType::LParen) && {
19399            // Look through nested parens for SELECT/WITH
19400            let mut depth = 0usize;
19401            let mut found_select = false;
19402            for i in 0..100 {
19403                match self.peek_nth(i).map(|t| t.token_type) {
19404                    Some(TokenType::LParen) => depth += 1,
19405                    Some(TokenType::Select) | Some(TokenType::With) if depth > 0 => {
19406                        found_select = true;
19407                        break;
19408                    }
19409                    _ => break,
19410                }
19411            }
19412            found_select
19413        } {
19414            // DESC (((SELECT ...))) — deeply nested parenthesized subquery
19415            self.parse_statement()?
19416        } else if matches!(
19417            self.config.dialect,
19418            Some(crate::dialects::DialectType::ClickHouse)
19419        ) && (self.check(TokenType::Insert)
19420            || self.check(TokenType::Create)
19421            || self.check(TokenType::Alter)
19422            || self.check(TokenType::Drop)
19423            || self.check(TokenType::Set)
19424            || self.check(TokenType::System))
19425        {
19426            self.parse_statement()?
19427        } else if matches!(
19428            self.config.dialect,
19429            Some(crate::dialects::DialectType::ClickHouse)
19430        ) && (self.is_identifier_token() || self.is_safe_keyword_as_identifier())
19431            && self.peek_nth(1).map(|t| t.token_type) == Some(TokenType::LParen)
19432        {
19433            // ClickHouse: DESC format(Values, '(123)') — function call as target
19434            self.parse_expression()?
19435        } else {
19436            // Parse as table reference
19437            let table = self.parse_table_ref()?;
19438            Expression::Table(Box::new(table))
19439        };
19440
19441        // Parse optional PARTITION clause (Spark/Hive)
19442        let partition = if self.match_token(TokenType::Partition) {
19443            // PARTITION(key = value, ...)
19444            self.expect(TokenType::LParen)?;
19445            // Parse partition expressions (e.g., ds = '2024-01-01')
19446            let mut partition_exprs = Vec::new();
19447            loop {
19448                if let Some(expr) = self.parse_conjunction()? {
19449                    partition_exprs.push(expr);
19450                }
19451                if !self.match_token(TokenType::Comma) {
19452                    break;
19453                }
19454            }
19455            self.expect(TokenType::RParen)?;
19456            let partition = Expression::Partition(Box::new(crate::expressions::Partition {
19457                expressions: partition_exprs,
19458                subpartition: false,
19459            }));
19460            Some(Box::new(partition))
19461        } else {
19462            None
19463        };
19464
19465        // ClickHouse: consume optional SETTINGS clause after target
19466        // e.g., DESC format(CSV, '...') SETTINGS key='val', key2='val2'
19467        if matches!(
19468            self.config.dialect,
19469            Some(crate::dialects::DialectType::ClickHouse)
19470        ) && self.check(TokenType::Settings)
19471        {
19472            self.skip(); // consume SETTINGS
19473            let _ = self.parse_settings_property()?;
19474        }
19475
19476        // Databricks: DESCRIBE ... AS JSON
19477        let as_json = if self.check(TokenType::As)
19478            && self
19479                .peek_nth(1)
19480                .map(|t| t.text.eq_ignore_ascii_case("JSON"))
19481                == Some(true)
19482        {
19483            self.skip(); // consume AS
19484            self.skip(); // consume JSON
19485            true
19486        } else {
19487            false
19488        };
19489
19490        // Parse optional post-target properties like type=stage (non-ClickHouse)
19491        if properties.is_empty() {
19492            while !self.is_at_end() && !self.check(TokenType::Semicolon) {
19493                // Check for identifier or keyword that could be a property name
19494                if self.check(TokenType::Var) || self.check(TokenType::Type) || self.check_keyword()
19495                {
19496                    let name = self.advance().text.to_lowercase();
19497                    if self.match_token(TokenType::Eq) {
19498                        let value = self.advance().text.clone();
19499                        properties.push((name, value));
19500                    } else {
19501                        // Not a property, put it back (can't easily undo, so break)
19502                        break;
19503                    }
19504                } else {
19505                    break;
19506                }
19507            }
19508        }
19509
19510        Ok(Expression::Describe(Box::new(Describe {
19511            target,
19512            extended,
19513            formatted,
19514            kind,
19515            properties,
19516            style,
19517            partition,
19518            leading_comments,
19519            as_json,
19520        })))
19521    }
19522
19523    /// Parse SHOW statement
19524    /// SHOW [TERSE] <object_type> [HISTORY] [LIKE pattern] [IN <scope>] [STARTS WITH pattern] [LIMIT n] [FROM object]
19525    fn parse_show(&mut self) -> Result<Expression> {
19526        self.expect(TokenType::Show)?;
19527
19528        // Check for TERSE
19529        let terse = self.match_identifier("TERSE");
19530
19531        // Parse the thing to show (DATABASES, TABLES, SCHEMAS, etc.)
19532        // This can be multiple words like "PRIMARY KEYS" or "IMPORTED KEYS"
19533        let mut this_parts = Vec::new();
19534        let mut target: Option<Expression> = None;
19535        let mut mutex: Option<bool> = None;
19536
19537        // Consume identifier tokens until we hit a keyword like LIKE, IN, FROM, LIMIT, HISTORY
19538        // Special handling for SingleStore SHOW variations
19539        while !self.is_at_end() {
19540            let current = self.peek();
19541            // Stop at keywords that start clauses
19542            if matches!(
19543                current.token_type,
19544                TokenType::Like
19545                    | TokenType::In
19546                    | TokenType::From
19547                    | TokenType::Limit
19548                    | TokenType::Semicolon
19549                    | TokenType::Eof
19550                    | TokenType::Where
19551                    | TokenType::For
19552                    | TokenType::Offset
19553                    | TokenType::Settings
19554            ) {
19555                // ClickHouse: SHOW CREATE SETTINGS PROFILE - don't stop at SETTINGS
19556                if current.token_type == TokenType::Settings
19557                    && matches!(
19558                        self.config.dialect,
19559                        Some(crate::dialects::DialectType::ClickHouse)
19560                    )
19561                    && this_parts.join(" ") == "CREATE"
19562                {
19563                    // Fall through to process SETTINGS as part of the type name
19564                } else {
19565                    break;
19566                }
19567            }
19568            // Handle comma-separated profile types (e.g., SHOW PROFILE BLOCK IO, PAGE FAULTS)
19569            // Append comma to the last part to preserve spacing
19570            if current.token_type == TokenType::Comma {
19571                if !this_parts.is_empty() {
19572                    let last = this_parts.pop().unwrap();
19573                    this_parts.push(format!("{},", last));
19574                }
19575                self.skip();
19576                continue;
19577            }
19578            // Stop at HISTORY keyword (but not as the first word)
19579            if !this_parts.is_empty() && current.text.eq_ignore_ascii_case("HISTORY") {
19580                break;
19581            }
19582            // Stop at STARTS keyword
19583            if current.text.eq_ignore_ascii_case("STARTS") {
19584                break;
19585            }
19586            // SingleStore: SHOW PLAN <id> - handle number directly (before Var/keyword check)
19587            // This is needed because numbers don't pass the Var/keyword check
19588            let joined_check = this_parts.join(" ");
19589            if joined_check == "PLAN" && current.token_type == TokenType::Number {
19590                let id = self.advance().text;
19591                target = Some(Expression::Literal(Literal::Number(id)));
19592                break;
19593            }
19594            // Accept identifiers and keywords as part of the object type
19595            if current.token_type == TokenType::Var || current.token_type.is_keyword() {
19596                let joined = this_parts.join(" ");
19597
19598                // SingleStore: SHOW CREATE <type> <name> - preserve case for name
19599                // Types: AGGREGATE, PIPELINE, PROJECTION
19600                if matches!(
19601                    joined.as_str(),
19602                    "CREATE AGGREGATE" | "CREATE PIPELINE" | "CREATE PROJECTION"
19603                ) {
19604                    let name = self.advance().text;
19605                    target = Some(Expression::Identifier(Identifier::new(name)));
19606                    break;
19607                }
19608
19609                // SingleStore: SHOW <type> ON <name> - preserve case for name after ON
19610                // Check if current token is "ON" (but not at start)
19611                if current.text.eq_ignore_ascii_case("ON") && !this_parts.is_empty() {
19612                    this_parts.push("ON".to_string());
19613                    self.skip();
19614                    // Parse the name after ON, preserving case
19615                    if !self.is_at_end() {
19616                        let next = self.peek();
19617                        // Handle "ON TABLE name" pattern
19618                        if next.text.eq_ignore_ascii_case("TABLE") {
19619                            this_parts.push("TABLE".to_string());
19620                            self.skip();
19621                        }
19622                        // Parse the actual name
19623                        if !self.is_at_end() {
19624                            let name_tok = self.peek();
19625                            if name_tok.token_type == TokenType::Var
19626                                || name_tok.token_type.is_keyword()
19627                            {
19628                                let name = self.advance().text;
19629                                target = Some(Expression::Identifier(Identifier::new(name)));
19630                            }
19631                        }
19632                    }
19633                    break;
19634                }
19635
19636                // SingleStore: SHOW REPRODUCTION INTO OUTFILE 'filename'
19637                if current.text.eq_ignore_ascii_case("INTO") && joined == "REPRODUCTION" {
19638                    this_parts.push("INTO".to_string());
19639                    self.skip();
19640                    if !self.is_at_end() && self.peek().text.eq_ignore_ascii_case("OUTFILE") {
19641                        this_parts.push("OUTFILE".to_string());
19642                        self.skip();
19643                        // Parse the filename
19644                        if !self.is_at_end() && self.check(TokenType::String) {
19645                            let filename = self.advance().text;
19646                            target = Some(Expression::Literal(Literal::String(filename)));
19647                        }
19648                    }
19649                    break;
19650                }
19651
19652                // SingleStore: SHOW PLAN [JSON] <id> - capture the numeric ID
19653                if joined == "PLAN" {
19654                    // Check if current is "JSON" - if so, push it and check for number
19655                    if current.text.eq_ignore_ascii_case("JSON") {
19656                        this_parts.push("JSON".to_string());
19657                        self.skip();
19658                        // Now check for number
19659                        if !self.is_at_end() && self.check(TokenType::Number) {
19660                            let id = self.advance().text;
19661                            target = Some(Expression::Literal(Literal::Number(id)));
19662                        }
19663                        break;
19664                    }
19665                    // Check if current is a number (plan ID)
19666                    if current.token_type == TokenType::Number {
19667                        let id = self.advance().text;
19668                        target = Some(Expression::Literal(Literal::Number(id)));
19669                        break;
19670                    }
19671                }
19672
19673                this_parts.push(current.text.to_ascii_uppercase());
19674                self.skip();
19675
19676                // ClickHouse: SHOW CREATE TABLE/VIEW/DICTIONARY <qualified_name>
19677                // After detecting CREATE TABLE/VIEW/DICTIONARY, parse the next as a table ref
19678                let joined = this_parts.join(" ");
19679                if matches!(
19680                    joined.as_str(),
19681                    "CREATE TABLE"
19682                        | "CREATE VIEW"
19683                        | "CREATE DICTIONARY"
19684                        | "CREATE DATABASE"
19685                        | "CREATE MATERIALIZED VIEW"
19686                        | "CREATE LIVE VIEW"
19687                ) {
19688                    if !self.is_at_end()
19689                        && (self.check(TokenType::Var)
19690                            || self.check(TokenType::QuotedIdentifier)
19691                            || self.is_safe_keyword_as_identifier())
19692                    {
19693                        let table = self.parse_table_ref()?;
19694                        target = Some(Expression::Table(Box::new(table)));
19695                    }
19696                    break;
19697                }
19698
19699                // ClickHouse: SHOW CREATE ROLE/PROFILE/QUOTA/ROW POLICY/POLICY with multi-name or ON clause
19700                // These have complex syntax (comma-separated names, ON db.table) - consume as raw text
19701                if matches!(
19702                    self.config.dialect,
19703                    Some(crate::dialects::DialectType::ClickHouse)
19704                ) && (matches!(
19705                    joined.as_str(),
19706                    "CREATE ROLE"
19707                        | "CREATE QUOTA"
19708                        | "CREATE SETTINGS PROFILE"
19709                        | "CREATE PROFILE"
19710                        | "CREATE ROW POLICY"
19711                        | "CREATE POLICY"
19712                        | "CREATE USER"
19713                ) || matches!(
19714                    joined.as_str(),
19715                    "SHOW CREATE ROLE"
19716                        | "SHOW CREATE QUOTA"
19717                        | "SHOW CREATE SETTINGS PROFILE"
19718                        | "SHOW CREATE PROFILE"
19719                        | "SHOW CREATE ROW POLICY"
19720                        | "SHOW CREATE POLICY"
19721                        | "SHOW CREATE USER"
19722                )) {
19723                    let mut parts = Vec::new();
19724                    while !self.is_at_end() && self.peek().token_type != TokenType::Semicolon {
19725                        parts.push(self.advance().text.clone());
19726                    }
19727                    target = Some(Expression::Identifier(Identifier::new(parts.join(" "))));
19728                    break;
19729                }
19730
19731                // ClickHouse: SHOW CREATE <qualified_name> (without TABLE/VIEW keyword)
19732                // e.g., SHOW CREATE INFORMATION_SCHEMA.COLUMNS
19733                if joined == "CREATE"
19734                    && matches!(
19735                        self.config.dialect,
19736                        Some(crate::dialects::DialectType::ClickHouse)
19737                    )
19738                    && !self.is_at_end()
19739                    && (self.check(TokenType::Var) || self.check(TokenType::QuotedIdentifier))
19740                    && !matches!(
19741                        self.peek().text.to_ascii_uppercase().as_str(),
19742                        "TABLE"
19743                            | "VIEW"
19744                            | "DICTIONARY"
19745                            | "DATABASE"
19746                            | "MATERIALIZED"
19747                            | "LIVE"
19748                            | "TEMPORARY"
19749                            | "ROLE"
19750                            | "QUOTA"
19751                            | "POLICY"
19752                            | "PROFILE"
19753                            | "USER"
19754                            | "ROW"
19755                            | "SETTINGS"
19756                    )
19757                {
19758                    let table = self.parse_table_ref()?;
19759                    target = Some(Expression::Table(Box::new(table)));
19760                    break;
19761                }
19762
19763                // Special handling for ENGINE: the next token is the engine name (case-preserved)
19764                // followed by STATUS or MUTEX
19765                if joined == "ENGINE" {
19766                    // Parse engine name (case-preserved)
19767                    if !self.is_at_end() {
19768                        let engine_tok = self.peek();
19769                        if engine_tok.token_type == TokenType::Var
19770                            || engine_tok.token_type.is_keyword()
19771                        {
19772                            let engine_name = self.advance().text;
19773                            target = Some(Expression::Identifier(Identifier::new(engine_name)));
19774                            // Parse STATUS or MUTEX
19775                            if !self.is_at_end() {
19776                                let next = self.peek();
19777                                let next_upper = next.text.to_ascii_uppercase();
19778                                if next_upper == "STATUS" {
19779                                    self.skip();
19780                                    mutex = Some(false);
19781                                } else if next_upper == "MUTEX" {
19782                                    self.skip();
19783                                    mutex = Some(true);
19784                                }
19785                            }
19786                        }
19787                    }
19788                    break;
19789                }
19790            } else {
19791                break;
19792            }
19793        }
19794
19795        let this = this_parts.join(" ");
19796
19797        // Check for HISTORY
19798        let history = self.match_identifier("HISTORY");
19799
19800        // Check for FOR target (MySQL: SHOW GRANTS FOR foo, SHOW PROFILE ... FOR QUERY 5)
19801        // SingleStore: SHOW GROUPS FOR ROLE 'role_name', SHOW GROUPS FOR USER 'username'
19802        let for_target = if self.match_token(TokenType::For) {
19803            // Parse the target (can be multi-word like QUERY 5, or ROLE 'name')
19804            let mut parts = Vec::new();
19805            while !self.is_at_end() {
19806                let tok = self.peek();
19807                if matches!(
19808                    tok.token_type,
19809                    TokenType::Like
19810                        | TokenType::In
19811                        | TokenType::From
19812                        | TokenType::Limit
19813                        | TokenType::Semicolon
19814                        | TokenType::Eof
19815                        | TokenType::Where
19816                ) {
19817                    break;
19818                }
19819                if tok.token_type == TokenType::Var
19820                    || tok.token_type.is_keyword()
19821                    || tok.token_type == TokenType::Number
19822                {
19823                    parts.push(self.advance().text);
19824                } else if tok.token_type == TokenType::String {
19825                    // Handle string literals (e.g., SHOW GROUPS FOR ROLE 'role_name')
19826                    let text = self.advance().text;
19827                    parts.push(format!("'{}'", text));
19828                } else {
19829                    break;
19830                }
19831            }
19832            if parts.is_empty() {
19833                None
19834            } else {
19835                Some(Expression::Identifier(Identifier::new(parts.join(" "))))
19836            }
19837        } else {
19838            None
19839        };
19840
19841        // Check for LIKE pattern
19842        let like = if self.match_token(TokenType::Like) {
19843            Some(self.parse_primary()?)
19844        } else {
19845            None
19846        };
19847
19848        // Check for IN scope
19849        let (scope_kind, scope) = if self.match_token(TokenType::In) {
19850            // Parse scope kind and optionally scope object
19851            // Check for keywords: ACCOUNT, DATABASE, SCHEMA, TABLE, CLASS, APPLICATION
19852            let (kind, scope_obj) = if self.match_keyword("ACCOUNT") {
19853                (Some("ACCOUNT".to_string()), None)
19854            } else if self.match_token(TokenType::Database) {
19855                // IN DATABASE [name]
19856                let scope_obj = if !self.is_at_end()
19857                    && !self.check(TokenType::Like)
19858                    && !self.check(TokenType::Limit)
19859                    && !self.check(TokenType::Semicolon)
19860                    && !self.check_keyword_text("STARTS")
19861                {
19862                    let table = self.parse_table_ref()?;
19863                    Some(Expression::Table(Box::new(table)))
19864                } else {
19865                    None
19866                };
19867                (Some("DATABASE".to_string()), scope_obj)
19868            } else if self.match_token(TokenType::Schema) {
19869                // IN SCHEMA [name]
19870                let scope_obj = if !self.is_at_end()
19871                    && !self.check(TokenType::Like)
19872                    && !self.check(TokenType::Limit)
19873                    && !self.check(TokenType::Semicolon)
19874                    && !self.check_keyword_text("STARTS")
19875                {
19876                    let table = self.parse_table_ref()?;
19877                    Some(Expression::Table(Box::new(table)))
19878                } else {
19879                    None
19880                };
19881                (Some("SCHEMA".to_string()), scope_obj)
19882            } else if self.match_token(TokenType::Table) {
19883                // IN TABLE [name]
19884                let scope_obj = if !self.is_at_end()
19885                    && !self.check(TokenType::Like)
19886                    && !self.check(TokenType::Limit)
19887                    && !self.check(TokenType::Semicolon)
19888                    && !self.check_keyword_text("STARTS")
19889                {
19890                    let table = self.parse_table_ref()?;
19891                    Some(Expression::Table(Box::new(table)))
19892                } else {
19893                    None
19894                };
19895                (Some("TABLE".to_string()), scope_obj)
19896            } else if self.match_token(TokenType::View) {
19897                // IN VIEW [name]
19898                let scope_obj = if !self.is_at_end()
19899                    && !self.check(TokenType::Like)
19900                    && !self.check(TokenType::Limit)
19901                    && !self.check(TokenType::Semicolon)
19902                    && !self.check_keyword_text("STARTS")
19903                {
19904                    let table = self.parse_table_ref()?;
19905                    Some(Expression::Table(Box::new(table)))
19906                } else {
19907                    None
19908                };
19909                (Some("VIEW".to_string()), scope_obj)
19910            } else if self.match_keyword("CLASS") {
19911                // IN CLASS name
19912                let scope_obj = if !self.is_at_end() {
19913                    let table = self.parse_table_ref()?;
19914                    Some(Expression::Table(Box::new(table)))
19915                } else {
19916                    None
19917                };
19918                (Some("CLASS".to_string()), scope_obj)
19919            } else if self.match_keyword("APPLICATION") {
19920                // IN APPLICATION [PACKAGE] name
19921                let kind = if self.match_keyword("PACKAGE") {
19922                    "APPLICATION PACKAGE".to_string()
19923                } else {
19924                    "APPLICATION".to_string()
19925                };
19926                let scope_obj = if !self.is_at_end() {
19927                    let table = self.parse_table_ref()?;
19928                    Some(Expression::Table(Box::new(table)))
19929                } else {
19930                    None
19931                };
19932                (Some(kind), scope_obj)
19933            } else {
19934                // Default - infer scope_kind based on what we're showing
19935                // Python SQLGlot: SCHEMA_KINDS = {"OBJECTS", "TABLES", "VIEWS", "SEQUENCES", "UNIQUE KEYS", "IMPORTED KEYS"}
19936                let table = self.parse_table_ref()?;
19937                let inferred_kind = match this.as_str() {
19938                    "OBJECTS" | "TABLES" | "VIEWS" | "SEQUENCES" | "UNIQUE KEYS"
19939                    | "IMPORTED KEYS" => "SCHEMA",
19940                    "PRIMARY KEYS" => "TABLE",
19941                    _ => "SCHEMA", // Default to SCHEMA for unknown types
19942                };
19943                (
19944                    Some(inferred_kind.to_string()),
19945                    Some(Expression::Table(Box::new(table))),
19946                )
19947            };
19948            (kind, scope_obj)
19949        } else {
19950            (None, None)
19951        };
19952
19953        // Check for STARTS WITH
19954        let starts_with = if self.match_keyword("STARTS") {
19955            self.match_token(TokenType::With); // WITH is a keyword token
19956            Some(self.parse_primary()?)
19957        } else {
19958            None
19959        };
19960
19961        // Check for LIMIT
19962        let limit = if self.match_token(TokenType::Limit) {
19963            Some(Box::new(Limit {
19964                this: self.parse_expression()?,
19965                percent: false,
19966                comments: Vec::new(),
19967            }))
19968        } else {
19969            None
19970        };
19971
19972        // Check for FROM (can be a string literal or identifier)
19973        // For MySQL SHOW COLUMNS/INDEX, the first FROM is the target table,
19974        // and the second FROM is the database
19975        let mut from = if self.match_token(TokenType::From) {
19976            Some(self.parse_primary()?)
19977        } else {
19978            None
19979        };
19980
19981        // Check for second FROM clause (MySQL: SHOW COLUMNS FROM tbl FROM db, SHOW INDEX FROM foo FROM bar)
19982        let mut db = if from.is_some() && self.match_token(TokenType::From) {
19983            Some(self.parse_primary()?)
19984        } else {
19985            None
19986        };
19987
19988        // Normalize MySQL SHOW INDEX/COLUMNS FROM db.tbl -> FROM tbl FROM db.
19989        if matches!(this.as_str(), "INDEX" | "COLUMNS") && db.is_none() {
19990            if let Some(from_expr) = from.take() {
19991                match from_expr {
19992                    Expression::Table(mut t) => {
19993                        if let Some(db_ident) = t.schema.take().or(t.catalog.take()) {
19994                            db = Some(Expression::Identifier(db_ident));
19995                            from = Some(Expression::Identifier(t.name));
19996                        } else {
19997                            from = Some(Expression::Table(t));
19998                        }
19999                    }
20000                    Expression::Column(c) => {
20001                        if let Some(table_ident) = c.table {
20002                            db = Some(Expression::Identifier(table_ident));
20003                            from = Some(Expression::Identifier(c.name));
20004                        } else {
20005                            from = Some(Expression::Column(c));
20006                        }
20007                    }
20008                    Expression::Identifier(id) => {
20009                        if let Some((db_name, table_name)) = id.name.split_once('.') {
20010                            db = Some(Expression::Identifier(Identifier::new(db_name)));
20011                            from = Some(Expression::Identifier(Identifier {
20012                                name: table_name.to_string(),
20013                                quoted: id.quoted,
20014                                trailing_comments: id.trailing_comments,
20015                                span: None,
20016                            }));
20017                        } else {
20018                            from = Some(Expression::Identifier(id));
20019                        }
20020                    }
20021                    other => {
20022                        from = Some(other);
20023                    }
20024                }
20025            }
20026        }
20027
20028        // MySQL: SHOW TABLES FROM db LIKE 'pattern' (LIKE can come after FROM)
20029        let like = if like.is_none() && self.match_token(TokenType::Like) {
20030            Some(self.parse_primary()?)
20031        } else {
20032            like
20033        };
20034
20035        // ClickHouse: SHOW ... NOT LIKE 'pattern' / NOT ILIKE 'pattern'
20036        if matches!(
20037            self.config.dialect,
20038            Some(crate::dialects::DialectType::ClickHouse)
20039        ) && self.check(TokenType::Not)
20040        {
20041            if self.current + 1 < self.tokens.len()
20042                && matches!(
20043                    self.tokens[self.current + 1].token_type,
20044                    TokenType::Like | TokenType::ILike
20045                )
20046            {
20047                self.skip(); // consume NOT
20048                self.skip(); // consume LIKE/ILIKE
20049                let _ = self.parse_primary()?; // consume pattern
20050            }
20051        }
20052
20053        // ClickHouse: SHOW ... ILIKE 'pattern'
20054        if matches!(
20055            self.config.dialect,
20056            Some(crate::dialects::DialectType::ClickHouse)
20057        ) && self.match_token(TokenType::ILike)
20058        {
20059            let _ = self.parse_primary()?; // consume pattern
20060        }
20061
20062        // Check for WHERE clause (MySQL: SHOW STATUS WHERE condition)
20063        let where_clause = if self.match_token(TokenType::Where) {
20064            Some(self.parse_expression()?)
20065        } else {
20066            None
20067        };
20068
20069        // Check for WITH PRIVILEGES clause (Snowflake: SHOW ... WITH PRIVILEGES USAGE, MODIFY)
20070        let privileges = if self.match_token(TokenType::With) && self.match_keyword("PRIVILEGES") {
20071            // Parse comma-separated list of privilege names (no parentheses)
20072            let mut privs = Vec::new();
20073            loop {
20074                if self.is_at_end() || self.check(TokenType::Semicolon) {
20075                    break;
20076                }
20077                let tok = self.peek();
20078                if tok.token_type == TokenType::Var || tok.token_type.is_keyword() {
20079                    privs.push(self.advance().text.to_ascii_uppercase());
20080                    // Check for comma to continue
20081                    if !self.match_token(TokenType::Comma) {
20082                        break;
20083                    }
20084                } else {
20085                    break;
20086                }
20087            }
20088            privs
20089        } else {
20090            Vec::new()
20091        };
20092
20093        // ClickHouse: SHOW ... SETTINGS key=val, key=val
20094        if matches!(
20095            self.config.dialect,
20096            Some(crate::dialects::DialectType::ClickHouse)
20097        ) {
20098            self.parse_clickhouse_settings_clause()?;
20099        }
20100
20101        Ok(Expression::Show(Box::new(Show {
20102            this,
20103            terse,
20104            history,
20105            like,
20106            scope_kind,
20107            scope,
20108            starts_with,
20109            limit,
20110            from,
20111            where_clause,
20112            for_target,
20113            db,
20114            target,
20115            mutex,
20116            privileges,
20117        })))
20118    }
20119
20120    /// Parse COPY statement (Snowflake, PostgreSQL)
20121    /// COPY INTO <table> FROM <source> [(<parameters>)]
20122    /// COPY INTO <location> FROM <table> [(<parameters>)]
20123    fn parse_copy(&mut self) -> Result<Expression> {
20124        self.expect(TokenType::Copy)?;
20125
20126        // Check for INTO (Snowflake/TSQL style: COPY INTO)
20127        let is_into = self.match_token(TokenType::Into);
20128
20129        // Parse target table or location (possibly with column list)
20130        let this = if self.check(TokenType::LParen) {
20131            // Subquery: COPY (SELECT ...) TO ...
20132            self.parse_primary()?
20133        } else if self.check(TokenType::DAt)
20134            || self.check(TokenType::String)
20135            || self.is_stage_reference()
20136        {
20137            // Stage or file destination (for exports): COPY INTO @stage or COPY INTO 's3://...'
20138            self.parse_file_location()?
20139        } else {
20140            // Table reference, possibly with column list: COPY table (col1, col2)
20141            let table = self.parse_table_ref()?;
20142            // Check for column list
20143            if self.check(TokenType::LParen) {
20144                // Peek ahead to see if this is a column list or a subquery
20145                // Column list won't start with SELECT
20146                let has_column_list = {
20147                    let start = self.current;
20148                    self.skip(); // consume (
20149                    let is_select = self.check(TokenType::Select);
20150                    self.current = start; // backtrack
20151                    !is_select
20152                };
20153                if has_column_list {
20154                    self.skip(); // consume (
20155                    let mut columns = Vec::new();
20156                    loop {
20157                        let col_name = self.expect_identifier_or_keyword()?;
20158                        columns.push(col_name);
20159                        if !self.match_token(TokenType::Comma) {
20160                            break;
20161                        }
20162                    }
20163                    self.expect(TokenType::RParen)?;
20164                    // Create a schema expression with the table and columns
20165                    Expression::Schema(Box::new(Schema {
20166                        this: Some(Box::new(Expression::Table(Box::new(table)))),
20167                        expressions: columns
20168                            .into_iter()
20169                            .map(|c| {
20170                                Expression::boxed_column(Column {
20171                                    name: Identifier::new(c),
20172                                    table: None,
20173                                    join_mark: false,
20174                                    trailing_comments: Vec::new(),
20175                                    span: None,
20176                                    inferred_type: None,
20177                                })
20178                            })
20179                            .collect(),
20180                    }))
20181                } else {
20182                    Expression::Table(Box::new(table))
20183                }
20184            } else {
20185                Expression::Table(Box::new(table))
20186            }
20187        };
20188
20189        // Determine direction: FROM means loading into table, TO means exporting
20190        let kind = self.match_token(TokenType::From);
20191        let has_to = if !kind {
20192            // Try TO keyword for export (TO is a keyword token, not an identifier)
20193            self.match_token(TokenType::To)
20194        } else {
20195            false
20196        };
20197
20198        // Parse source/destination files or stage only if FROM/TO was found
20199        // and we're not at a parameter (which would start with identifier = ...)
20200        let mut files = Vec::new();
20201        if kind
20202            || has_to
20203            || self.check(TokenType::String)
20204            || self.is_stage_reference()
20205            || self.check(TokenType::LParen)
20206        {
20207            // Check for subquery: FROM (SELECT ...)
20208            if self.check(TokenType::LParen) {
20209                // Peek ahead to see if this is a subquery
20210                let start = self.current;
20211                self.skip(); // consume (
20212                let is_select = self.check(TokenType::Select);
20213                self.current = start; // backtrack
20214                if is_select {
20215                    // Parse the subquery
20216                    let subquery = self.parse_primary()?;
20217                    files.push(subquery);
20218                }
20219            }
20220            // Parse file location(s) until we hit a parameter or end
20221            while !self.is_at_end() && !self.check(TokenType::Semicolon) && files.is_empty()
20222                || (self.check(TokenType::Comma) && !files.is_empty())
20223            {
20224                // Consume comma if present (for multiple files)
20225                if !files.is_empty() && !self.match_token(TokenType::Comma) {
20226                    break;
20227                }
20228                // Check if this looks like a parameter (identifier followed by =)
20229                // But stage references (@stage) are not parameters
20230                if (self.check(TokenType::Var) || self.check_keyword())
20231                    && !self.is_stage_reference()
20232                {
20233                    let lookahead = self.current + 1;
20234                    if lookahead < self.tokens.len()
20235                        && self.tokens[lookahead].token_type == TokenType::Eq
20236                    {
20237                        break; // This is a parameter, stop parsing files
20238                    }
20239                }
20240                // Check for WITH keyword - stop parsing files
20241                if self.check(TokenType::With) {
20242                    break;
20243                }
20244                // Stop if we don't see a file location start
20245                // Include QuotedIdentifier for Databricks backtick-quoted paths like `s3://link`
20246                if !self.check(TokenType::String)
20247                    && !self.is_stage_reference()
20248                    && !self.check(TokenType::Var)
20249                    && !self.check_keyword()
20250                    && !self.check(TokenType::QuotedIdentifier)
20251                {
20252                    break;
20253                }
20254                // For COPY INTO ... FROM table_name, handle dotted table references
20255                // If the next token is a Var/Identifier and the one after is a Dot, parse as table reference
20256                if (self.check(TokenType::Var) || self.is_identifier_token())
20257                    && !self.is_stage_reference()
20258                {
20259                    let lookahead = self.current + 1;
20260                    let has_dot = lookahead < self.tokens.len()
20261                        && self.tokens[lookahead].token_type == TokenType::Dot;
20262                    if has_dot {
20263                        let table = self.parse_table_ref()?;
20264                        files.push(Expression::Table(Box::new(table)));
20265                        continue;
20266                    }
20267                }
20268                let location = self.parse_file_location()?;
20269                files.push(location);
20270            }
20271        }
20272
20273        // Parse credentials and parameters
20274        let mut params = Vec::new();
20275        let mut credentials = None;
20276        let mut with_wrapped = false;
20277
20278        // Parse Snowflake-style parameters: KEY = VALUE or KEY = (nested values)
20279        // or DuckDB/PostgreSQL WITH (KEY VALUE, ...) format
20280        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
20281            // Match WITH keyword if present (some dialects use WITH before params)
20282            let had_with = self.match_token(TokenType::With);
20283
20284            // Check for wrapped parameters in parentheses
20285            if self.match_token(TokenType::LParen) {
20286                if had_with {
20287                    with_wrapped = true;
20288                }
20289                while !self.check(TokenType::RParen) && !self.is_at_end() {
20290                    let param = self.parse_copy_parameter()?;
20291                    params.push(param);
20292                    // Consume optional comma between params
20293                    self.match_token(TokenType::Comma);
20294                }
20295                self.expect(TokenType::RParen)?;
20296                break;
20297            }
20298
20299            // Parse individual parameter: NAME = value
20300            if self.check(TokenType::Var) || self.check_keyword() {
20301                let param = self.parse_copy_parameter()?;
20302
20303                // Handle special CREDENTIALS parameter (case-insensitive)
20304                if param.name.eq_ignore_ascii_case("CREDENTIALS") {
20305                    // For Redshift-style CREDENTIALS 'string' (single string value)
20306                    // vs Snowflake-style CREDENTIALS = (KEY='value', KEY2='value')
20307                    if let Some(Expression::Literal(Literal::String(s))) = &param.value {
20308                        // Redshift style: store as a simple credentials string
20309                        let creds = Credentials {
20310                            credentials: vec![("".to_string(), s.clone())],
20311                            storage: None,
20312                            encryption: None,
20313                        };
20314                        credentials = Some(Box::new(creds));
20315                    } else {
20316                        // Snowflake style: key=value pairs
20317                        let creds = Credentials {
20318                            credentials: param
20319                                .values
20320                                .iter()
20321                                .filter_map(|v| {
20322                                    if let Expression::Eq(eq) = v {
20323                                        let key = if let Expression::Column(c) = &eq.left {
20324                                            c.name.name.clone()
20325                                        } else {
20326                                            return None;
20327                                        };
20328                                        let val = if let Expression::Literal(Literal::String(s)) =
20329                                            &eq.right
20330                                        {
20331                                            s.clone()
20332                                        } else {
20333                                            return None;
20334                                        };
20335                                        Some((key, val))
20336                                    } else {
20337                                        None
20338                                    }
20339                                })
20340                                .collect(),
20341                            storage: None,
20342                            encryption: None,
20343                        };
20344                        credentials = Some(Box::new(creds));
20345                    }
20346                } else if param.name.eq_ignore_ascii_case("STORAGE_INTEGRATION") {
20347                    // Store STORAGE_INTEGRATION as a regular parameter only
20348                    // Don't use the credentials.storage field for this
20349                    params.push(param);
20350                } else {
20351                    params.push(param);
20352                }
20353            } else {
20354                break;
20355            }
20356        }
20357
20358        Ok(Expression::Copy(Box::new(CopyStmt {
20359            this,
20360            kind,
20361            files,
20362            params,
20363            credentials,
20364            is_into,
20365            with_wrapped,
20366        })))
20367    }
20368
20369    /// Parse a single COPY parameter: NAME = value, NAME = (nested values), or NAME value (no =)
20370    fn parse_copy_parameter(&mut self) -> Result<CopyParameter> {
20371        // Preserve original case for parameter name (important for Redshift COPY options)
20372        let name = self.expect_identifier_or_keyword()?;
20373
20374        let mut value = None;
20375        let mut values = Vec::new();
20376
20377        let has_eq = self.match_token(TokenType::Eq);
20378
20379        if has_eq {
20380            if self.match_token(TokenType::LParen) {
20381                // Nested parameter list: KEY = (nested_key=value, ...) or KEY = (value1, value2)
20382                // Check if this is a list of simple values (like strings) or key=value pairs
20383                // If the first token is a string/number, it's a list of values
20384                if self.check(TokenType::String) || self.check(TokenType::Number) {
20385                    // Simple value list: FILES = ('test1.csv', 'test2.csv')
20386                    while !self.check(TokenType::RParen) && !self.is_at_end() {
20387                        values.push(self.parse_primary()?);
20388                        if !self.match_token(TokenType::Comma) {
20389                            break;
20390                        }
20391                    }
20392                } else {
20393                    // Key=value pairs: CREDENTIALS = (AWS_KEY_ID='id' AWS_SECRET_KEY='key')
20394                    while !self.check(TokenType::RParen) && !self.is_at_end() {
20395                        // Parse nested key=value pairs
20396                        let nested_key = self.expect_identifier_or_keyword()?.to_ascii_uppercase();
20397                        if self.match_token(TokenType::Eq) {
20398                            let nested_value = self.parse_copy_param_value()?;
20399                            // Create an Eq expression for the nested key=value
20400                            values.push(Expression::Eq(Box::new(BinaryOp {
20401                                left: Expression::boxed_column(Column {
20402                                    name: Identifier::new(nested_key),
20403                                    table: None,
20404                                    join_mark: false,
20405                                    trailing_comments: Vec::new(),
20406                                    span: None,
20407                                    inferred_type: None,
20408                                }),
20409                                right: nested_value,
20410                                left_comments: Vec::new(),
20411                                operator_comments: Vec::new(),
20412                                trailing_comments: Vec::new(),
20413                                inferred_type: None,
20414                            })));
20415                        } else {
20416                            // Just a keyword/value without =
20417                            values.push(Expression::boxed_column(Column {
20418                                name: Identifier::new(nested_key),
20419                                table: None,
20420                                join_mark: false,
20421                                trailing_comments: Vec::new(),
20422                                span: None,
20423                                inferred_type: None,
20424                            }));
20425                        }
20426                        // Consume optional comma between nested values
20427                        self.match_token(TokenType::Comma);
20428                    }
20429                }
20430                self.expect(TokenType::RParen)?;
20431            } else {
20432                // Simple value: KEY = value
20433                value = Some(self.parse_copy_param_value()?);
20434            }
20435        } else {
20436            // No = sign: DuckDB/PostgreSQL format (KEY value or KEY (col1, col2))
20437            // Check if followed by a value: string, number, boolean, identifier, or tuple
20438            if self.check(TokenType::LParen) {
20439                // Check if this is a COPY_INTO_VARLEN_OPTIONS parameter
20440                // These are Databricks/Snowflake options that contain key='value' pairs without = before (
20441                let is_varlen_option = matches!(
20442                    name.as_str(),
20443                    "FORMAT_OPTIONS" | "COPY_OPTIONS" | "FILE_FORMAT" | "CREDENTIAL"
20444                );
20445
20446                self.skip(); // consume (
20447
20448                if is_varlen_option {
20449                    // Parse as key='value' pairs: FORMAT_OPTIONS ('opt1'='true', 'opt2'='test')
20450                    while !self.check(TokenType::RParen) && !self.is_at_end() {
20451                        if self.check(TokenType::String) {
20452                            // Parse 'key'='value' pair
20453                            let key_token = self.advance();
20454                            let key = key_token.text.clone();
20455                            if self.match_token(TokenType::Eq) {
20456                                let val = self.parse_copy_param_value()?;
20457                                values.push(Expression::Eq(Box::new(BinaryOp {
20458                                    left: Expression::Literal(Literal::String(key)),
20459                                    right: val,
20460                                    left_comments: Vec::new(),
20461                                    operator_comments: Vec::new(),
20462                                    trailing_comments: Vec::new(),
20463                                    inferred_type: None,
20464                                })));
20465                            } else {
20466                                // Just a string without =
20467                                values.push(Expression::Literal(Literal::String(key)));
20468                            }
20469                        } else if self.check(TokenType::Var)
20470                            || self.check_keyword()
20471                            || self.is_identifier_token()
20472                        {
20473                            // Parse identifier='value' pair (unquoted key)
20474                            let key = self.advance().text.clone();
20475                            if self.match_token(TokenType::Eq) {
20476                                let val = self.parse_copy_param_value()?;
20477                                values.push(Expression::Eq(Box::new(BinaryOp {
20478                                    left: Expression::boxed_column(Column {
20479                                        name: Identifier::new(key),
20480                                        table: None,
20481                                        join_mark: false,
20482                                        trailing_comments: Vec::new(),
20483                                        span: None,
20484                                        inferred_type: None,
20485                                    }),
20486                                    right: val,
20487                                    left_comments: Vec::new(),
20488                                    operator_comments: Vec::new(),
20489                                    trailing_comments: Vec::new(),
20490                                    inferred_type: None,
20491                                })));
20492                            } else {
20493                                // Just an identifier without =
20494                                values.push(Expression::boxed_column(Column {
20495                                    name: Identifier::new(key),
20496                                    table: None,
20497                                    join_mark: false,
20498                                    trailing_comments: Vec::new(),
20499                                    span: None,
20500                                    inferred_type: None,
20501                                }));
20502                            }
20503                        } else {
20504                            break;
20505                        }
20506                        self.match_token(TokenType::Comma);
20507                    }
20508                } else {
20509                    // Tuple value: FORCE_NOT_NULL (col1, col2)
20510                    let mut items = Vec::new();
20511                    while !self.check(TokenType::RParen) && !self.is_at_end() {
20512                        items.push(self.parse_primary()?);
20513                        if !self.match_token(TokenType::Comma) {
20514                            break;
20515                        }
20516                    }
20517                    value = Some(Expression::Tuple(Box::new(Tuple { expressions: items })));
20518                }
20519                self.expect(TokenType::RParen)?;
20520            } else if self.check(TokenType::LBrace) {
20521                // Map literal: KV_METADATA {'key': 'value', ...}
20522                value = Some(self.parse_primary()?);
20523            } else if self.check(TokenType::String) || self.check(TokenType::Number) {
20524                // String or number value
20525                value = Some(self.parse_copy_param_value()?);
20526            } else if self.check(TokenType::True) || self.check(TokenType::False) {
20527                // Boolean value (TRUE/FALSE are keyword tokens)
20528                value = Some(self.parse_copy_param_value()?);
20529            } else if !self.check(TokenType::Comma)
20530                && !self.check(TokenType::RParen)
20531                && !self.is_at_end()
20532                && !self.check(TokenType::Semicolon)
20533            {
20534                // Identifier value: FORMAT JSON, HEADER MATCH, etc.
20535                // But skip if this is a known flag-only parameter (Redshift COPY options that take no value)
20536                let name_upper = name.to_ascii_uppercase();
20537                let is_flag_param = matches!(
20538                    name_upper.as_str(),
20539                    "EMPTYASNULL"
20540                        | "BLANKSASNULL"
20541                        | "ACCEPTINVCHARS"
20542                        | "COMPUPDATE"
20543                        | "STATUPDATE"
20544                        | "NOLOAD"
20545                        | "ESCAPE"
20546                        | "REMOVEQUOTES"
20547                        | "EXPLICIT_IDS"
20548                        | "FILLRECORD"
20549                        | "TRIMBLANKS"
20550                        | "TRUNCATECOLUMNS"
20551                        | "ROUNDEC"
20552                        | "IGNOREHEADER"
20553                        | "IGNOREBLANKLINES"
20554                        | "ACCEPTANYDATE"
20555                );
20556                if !is_flag_param && (self.check(TokenType::Var) || self.check_keyword()) {
20557                    value = Some(self.parse_copy_param_value()?);
20558                }
20559            }
20560            // If nothing matched, it's a bare flag parameter with no value (allowed)
20561        }
20562
20563        Ok(CopyParameter {
20564            name,
20565            value,
20566            values,
20567            eq: has_eq,
20568        })
20569    }
20570
20571    /// Parse a value for COPY parameters (handles strings, identifiers, numbers, lists)
20572    fn parse_copy_param_value(&mut self) -> Result<Expression> {
20573        // Handle lists like ('file1', 'file2')
20574        if self.match_token(TokenType::LParen) {
20575            let mut items = Vec::new();
20576            while !self.check(TokenType::RParen) && !self.is_at_end() {
20577                items.push(self.parse_primary()?);
20578                if !self.match_token(TokenType::Comma) {
20579                    break;
20580                }
20581            }
20582            self.expect(TokenType::RParen)?;
20583            return Ok(Expression::Tuple(Box::new(Tuple { expressions: items })));
20584        }
20585
20586        // Handle string, number, boolean, identifier
20587        if self.check(TokenType::String) {
20588            let token = self.advance();
20589            return Ok(Expression::Literal(Literal::String(token.text.clone())));
20590        }
20591        // Handle quoted identifier (e.g., STORAGE_INTEGRATION = "storage")
20592        if self.check(TokenType::QuotedIdentifier) {
20593            let token = self.advance();
20594            return Ok(Expression::boxed_column(Column {
20595                name: Identifier::quoted(token.text.clone()),
20596                table: None,
20597                join_mark: false,
20598                trailing_comments: Vec::new(),
20599                span: None,
20600                inferred_type: None,
20601            }));
20602        }
20603        if self.check(TokenType::Number) {
20604            let token = self.advance();
20605            return Ok(Expression::Literal(Literal::Number(token.text.clone())));
20606        }
20607        if self.match_token(TokenType::True) {
20608            return Ok(Expression::Boolean(BooleanLiteral { value: true }));
20609        }
20610        if self.match_token(TokenType::False) {
20611            return Ok(Expression::Boolean(BooleanLiteral { value: false }));
20612        }
20613        // Identifier (e.g., FORMAT_NAME=my_format)
20614        if self.check(TokenType::Var) || self.check_keyword() {
20615            // Could be a qualified name like MY_DATABASE.MY_SCHEMA.MY_FORMAT
20616            let first = self.advance().text.clone();
20617            if self.match_token(TokenType::Dot) {
20618                let second = self.expect_identifier_or_keyword()?;
20619                if self.match_token(TokenType::Dot) {
20620                    let third = self.expect_identifier_or_keyword()?;
20621                    return Ok(Expression::boxed_column(Column {
20622                        name: Identifier::new(format!("{}.{}.{}", first, second, third)),
20623                        table: None,
20624                        join_mark: false,
20625                        trailing_comments: Vec::new(),
20626                        span: None,
20627                        inferred_type: None,
20628                    }));
20629                }
20630                return Ok(Expression::boxed_column(Column {
20631                    name: Identifier::new(format!("{}.{}", first, second)),
20632                    table: None,
20633                    join_mark: false,
20634                    trailing_comments: Vec::new(),
20635                    span: None,
20636                    inferred_type: None,
20637                }));
20638            }
20639            return Ok(Expression::boxed_column(Column {
20640                name: Identifier::new(first),
20641                table: None,
20642                join_mark: false,
20643                trailing_comments: Vec::new(),
20644                span: None,
20645                inferred_type: None,
20646            }));
20647        }
20648
20649        Err(self.parse_error("Expected value for COPY parameter"))
20650    }
20651
20652    /// Parse Snowflake stage reference when tokenized as String (e.g., '@mystage', '@external/location')
20653    /// Handles: '@mystage', '@external/location'
20654    fn parse_stage_reference_from_string(&mut self) -> Result<Expression> {
20655        use crate::expressions::StageReference;
20656
20657        // The String token contains @ and the entire path
20658        let string_token = self.advance();
20659        let full_path = string_token.text.clone();
20660
20661        // Split on / to get stage name and path
20662        let parts: Vec<&str> = full_path.splitn(2, '/').collect();
20663        let name = parts[0].to_string();
20664        let path = if parts.len() > 1 {
20665            Some(format!("/{}", parts[1]))
20666        } else {
20667            None
20668        };
20669
20670        // Handle optional parameters: (FILE_FORMAT => 'fmt', PATTERN => '*.csv')
20671        let (file_format, pattern) = if self.match_token(TokenType::LParen) {
20672            let mut ff = None;
20673            let mut pat = None;
20674
20675            loop {
20676                if self.match_identifier("FILE_FORMAT") {
20677                    self.expect(TokenType::FArrow)?; // =>
20678                    ff = Some(self.parse_primary()?);
20679                } else if self.match_identifier("PATTERN") || self.match_token(TokenType::Pattern) {
20680                    // PATTERN can be tokenized as keyword or identifier
20681                    self.expect(TokenType::FArrow)?; // =>
20682                    if let Expression::Literal(Literal::String(s)) = self.parse_primary()? {
20683                        pat = Some(s);
20684                    }
20685                } else {
20686                    break;
20687                }
20688
20689                if !self.match_token(TokenType::Comma) {
20690                    break;
20691                }
20692            }
20693
20694            self.expect(TokenType::RParen)?;
20695            (ff, pat)
20696        } else {
20697            (None, None)
20698        };
20699
20700        Ok(Expression::StageReference(Box::new(StageReference {
20701            name,
20702            path,
20703            file_format,
20704            pattern,
20705            quoted: true, // Stage reference came from a quoted string
20706        })))
20707    }
20708
20709    /// Parse Snowflake stage reference when tokenized as Var (e.g., @mystage becomes Var token)
20710    /// Handles: @mystage, @mystage/path/to/file.csv
20711    fn parse_stage_reference_from_var(&mut self) -> Result<Expression> {
20712        use crate::expressions::StageReference;
20713
20714        // The Var token already contains @ and the stage name
20715        let var_token = self.advance();
20716        let mut name = var_token.text.clone();
20717
20718        // Handle qualified names: @namespace.stage
20719        while self.match_token(TokenType::Dot) {
20720            name.push('.');
20721            if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
20722                name.push_str(&self.advance().text);
20723            } else if self.check(TokenType::Percent) {
20724                // Handle table stage in qualified path: @namespace.%table_name
20725                self.skip();
20726                name.push('%');
20727                if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
20728                    name.push_str(&self.advance().text);
20729                }
20730            } else {
20731                break;
20732            }
20733        }
20734
20735        // Handle path after stage: @stage/path/to/file.csv
20736        let path = if self.match_token(TokenType::Slash) {
20737            let mut path_str = String::from("/");
20738            // Consume path components until we hit whitespace/paren/etc.
20739            while !self.is_at_end() {
20740                if self.check(TokenType::Identifier)
20741                    || self.check(TokenType::Var)
20742                    || self.check(TokenType::Number)
20743                    || self.check(TokenType::Dot)
20744                    || self.check(TokenType::Dash)
20745                    || self.check(TokenType::Star)
20746                    || self.check(TokenType::To)
20747                    || self.is_safe_keyword_as_identifier()
20748                {
20749                    path_str.push_str(&self.advance().text);
20750                } else if self.match_token(TokenType::Slash) {
20751                    path_str.push('/');
20752                } else {
20753                    break;
20754                }
20755            }
20756            Some(path_str)
20757        } else {
20758            None
20759        };
20760
20761        // Handle optional parameters: (FILE_FORMAT => 'fmt', PATTERN => '*.csv')
20762        let (file_format, pattern) = if self.match_token(TokenType::LParen) {
20763            let mut ff = None;
20764            let mut pat = None;
20765
20766            loop {
20767                if self.match_identifier("FILE_FORMAT") {
20768                    self.expect(TokenType::FArrow)?; // =>
20769                    ff = Some(self.parse_primary()?);
20770                } else if self.match_identifier("PATTERN") || self.match_token(TokenType::Pattern) {
20771                    // PATTERN can be tokenized as keyword or identifier
20772                    self.expect(TokenType::FArrow)?; // =>
20773                    if let Expression::Literal(Literal::String(s)) = self.parse_primary()? {
20774                        pat = Some(s);
20775                    }
20776                } else {
20777                    break;
20778                }
20779
20780                if !self.match_token(TokenType::Comma) {
20781                    break;
20782                }
20783            }
20784
20785            self.expect(TokenType::RParen)?;
20786            (ff, pat)
20787        } else {
20788            (None, None)
20789        };
20790
20791        Ok(Expression::StageReference(Box::new(StageReference {
20792            name,
20793            path,
20794            file_format,
20795            pattern,
20796            quoted: false,
20797        })))
20798    }
20799
20800    /// Parse Snowflake stage reference in FROM clause
20801    /// Handles: @stage, @"stage", @namespace.stage, @stage/path/file.csv, @~, @%table
20802    fn parse_stage_reference(&mut self) -> Result<Expression> {
20803        use crate::expressions::StageReference;
20804
20805        self.expect(TokenType::DAt)?; // consume @
20806
20807        // Build the stage name - can include dots, slashes, etc.
20808        let mut name = String::from("@");
20809
20810        // Handle special stage types:
20811        // @~ = user stage
20812        // @% = table stage (followed by table name)
20813        if self.check(TokenType::Tilde) {
20814            self.skip();
20815            name.push('~');
20816        } else if self.check(TokenType::Percent) {
20817            self.skip();
20818            name.push('%');
20819            // Table name follows (can be qualified: schema.table)
20820            loop {
20821                if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
20822                    name.push_str(&self.advance().text);
20823                } else {
20824                    break;
20825                }
20826                // Handle qualified table names: %db.schema.table
20827                if self.match_token(TokenType::Dot) {
20828                    name.push('.');
20829                } else {
20830                    break;
20831                }
20832            }
20833        } else {
20834            // Handle quoted or unquoted stage names
20835            loop {
20836                if self.check(TokenType::QuotedIdentifier) {
20837                    // Preserve quotes for quoted identifiers
20838                    let text = self.advance().text;
20839                    name.push('"');
20840                    name.push_str(&text);
20841                    name.push('"');
20842                } else if self.check(TokenType::Percent) {
20843                    // Handle table stage in qualified path: @namespace.%table_name
20844                    self.skip();
20845                    name.push('%');
20846                    if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
20847                        name.push_str(&self.advance().text);
20848                    }
20849                } else if self.check(TokenType::Identifier)
20850                    || self.check(TokenType::Var)
20851                    || self.is_safe_keyword_as_identifier()
20852                {
20853                    name.push_str(&self.advance().text);
20854                } else {
20855                    break;
20856                }
20857
20858                // Handle dots for qualified names: @namespace.stage or @"schema"."stage"
20859                if self.match_token(TokenType::Dot) {
20860                    name.push('.');
20861                } else {
20862                    break;
20863                }
20864            }
20865        }
20866
20867        // Handle path after stage: @stage/path/to/file.csv
20868        let path = if self.match_token(TokenType::Slash) {
20869            let mut path_str = String::from("/");
20870            // Consume path components until we hit whitespace/paren/etc.
20871            // Note: path can include keywords like 'to', 'data', etc.
20872            while !self.is_at_end() {
20873                if self.check(TokenType::Identifier)
20874                    || self.check(TokenType::Var)
20875                    || self.check(TokenType::Number)
20876                    || self.check(TokenType::Dot)
20877                    || self.check(TokenType::Dash)
20878                    || self.check(TokenType::Star)
20879                    || self.check(TokenType::To)
20880                    || self.is_safe_keyword_as_identifier()
20881                {
20882                    path_str.push_str(&self.advance().text);
20883                } else if self.match_token(TokenType::Slash) {
20884                    path_str.push('/');
20885                } else {
20886                    break;
20887                }
20888            }
20889            Some(path_str)
20890        } else {
20891            None
20892        };
20893
20894        // Handle optional parameters: (FILE_FORMAT => 'fmt', PATTERN => '*.csv')
20895        let (file_format, pattern) = if self.match_token(TokenType::LParen) {
20896            let mut ff = None;
20897            let mut pat = None;
20898
20899            loop {
20900                if self.match_identifier("FILE_FORMAT") {
20901                    self.expect(TokenType::FArrow)?; // =>
20902                    ff = Some(self.parse_primary()?);
20903                } else if self.match_identifier("PATTERN") || self.match_token(TokenType::Pattern) {
20904                    // PATTERN can be tokenized as keyword or identifier
20905                    self.expect(TokenType::FArrow)?; // =>
20906                    if let Expression::Literal(Literal::String(s)) = self.parse_primary()? {
20907                        pat = Some(s);
20908                    }
20909                } else {
20910                    break;
20911                }
20912
20913                if !self.match_token(TokenType::Comma) {
20914                    break;
20915                }
20916            }
20917
20918            self.expect(TokenType::RParen)?;
20919            (ff, pat)
20920        } else {
20921            (None, None)
20922        };
20923
20924        Ok(Expression::StageReference(Box::new(StageReference {
20925            name,
20926            path,
20927            file_format,
20928            pattern,
20929            quoted: false,
20930        })))
20931    }
20932
20933    /// Parse file location for COPY/PUT statements
20934    /// Handles: @stage, @db.schema.stage, @stage/path, 's3://bucket/path', file:///path
20935    fn parse_file_location(&mut self) -> Result<Expression> {
20936        // Stage reference starting with @ (tokenized as DAt or as a Var starting with @)
20937        if self.check(TokenType::DAt) {
20938            self.skip(); // consume @
20939            let mut stage_path = String::from("@");
20940
20941            // Handle table stage prefix: @%table
20942            if self.check(TokenType::Percent) || self.check(TokenType::Mod) {
20943                stage_path.push('%');
20944                self.skip(); // consume %
20945            }
20946            // Handle user stage: @~
20947            else if self.check(TokenType::Tilde) {
20948                stage_path.push('~');
20949                self.skip(); // consume ~
20950            }
20951
20952            // Get stage name
20953            if self.check(TokenType::Var) || self.check_keyword() || self.is_identifier_token() {
20954                stage_path.push_str(&self.advance().text);
20955            }
20956            // Parse qualified name parts: .schema.stage
20957            while self.check(TokenType::Dot) {
20958                self.skip(); // consume .
20959                stage_path.push('.');
20960                if self.check(TokenType::Var) || self.check_keyword() || self.is_identifier_token()
20961                {
20962                    stage_path.push_str(&self.advance().text);
20963                }
20964            }
20965            // Parse path segments: /path/to/file (slash is tokenized separately)
20966            while self.check(TokenType::Slash) {
20967                self.skip(); // consume /
20968                stage_path.push('/');
20969                // Get path segment (identifier, keyword, or special chars)
20970                // But don't consume if followed by = (that's a parameter, not path)
20971                if (self.check(TokenType::Var)
20972                    || self.check_keyword()
20973                    || self.is_identifier_token())
20974                    && !self.check_next(TokenType::Eq)
20975                {
20976                    stage_path.push_str(&self.advance().text);
20977                }
20978            }
20979            return Ok(Expression::Literal(Literal::String(stage_path)));
20980        }
20981
20982        // Stage reference tokenized as a Var starting with @ (e.g., @random_stage)
20983        // This happens when the tokenizer combines @ with the following identifier
20984        if self.check(TokenType::Var) && self.peek().text.starts_with('@') {
20985            let mut stage_path = self.advance().text.clone();
20986            // Parse qualified name parts: .schema.stage
20987            while self.check(TokenType::Dot) {
20988                self.skip(); // consume .
20989                stage_path.push('.');
20990                if self.check(TokenType::Var) || self.check_keyword() || self.is_identifier_token()
20991                {
20992                    stage_path.push_str(&self.advance().text);
20993                }
20994            }
20995            // Parse path segments: /path/to/file
20996            while self.check(TokenType::Slash) {
20997                self.skip(); // consume /
20998                stage_path.push('/');
20999                // Get path segment (identifier, keyword, or special chars)
21000                // But don't consume if followed by = (that's a parameter, not path)
21001                if (self.check(TokenType::Var)
21002                    || self.check_keyword()
21003                    || self.is_identifier_token())
21004                    && !self.check_next(TokenType::Eq)
21005                {
21006                    stage_path.push_str(&self.advance().text);
21007                }
21008            }
21009            return Ok(Expression::Literal(Literal::String(stage_path)));
21010        }
21011
21012        // String literal (file path or URL)
21013        if self.check(TokenType::String) {
21014            let token = self.advance();
21015            return Ok(Expression::Literal(Literal::String(token.text.clone())));
21016        }
21017
21018        // Backtick-quoted identifier (Databricks style: `s3://link`)
21019        if self.check(TokenType::QuotedIdentifier) {
21020            let token = self.advance();
21021            return Ok(Expression::Identifier(Identifier::quoted(
21022                token.text.clone(),
21023            )));
21024        }
21025
21026        // Identifier (could be a stage name without @)
21027        if self.check(TokenType::Var) || self.check_keyword() {
21028            let ident = self.advance().text.clone();
21029            return Ok(Expression::boxed_column(Column {
21030                name: Identifier::new(ident),
21031                table: None,
21032                join_mark: false,
21033                trailing_comments: Vec::new(),
21034                span: None,
21035                inferred_type: None,
21036            }));
21037        }
21038
21039        Err(self.parse_error("Expected file location"))
21040    }
21041
21042    /// Parse Snowflake stage reference as a string for PUT/GET/COPY statements
21043    /// Handles: @stage, @%table, @~, @db.schema.stage, @"quoted"."stage", @stage/path
21044    /// Returns a Literal::String containing the stage path
21045    fn parse_stage_reference_as_string(&mut self) -> Result<Expression> {
21046        // Stage reference starting with @ (tokenized as DAt)
21047        if self.check(TokenType::DAt) {
21048            self.skip(); // consume @
21049            let mut stage_path = String::from("@");
21050
21051            // Handle table stage prefix: @%table
21052            if self.check(TokenType::Percent) || self.check(TokenType::Mod) {
21053                stage_path.push('%');
21054                self.skip(); // consume %
21055            }
21056            // Handle user stage: @~
21057            else if self.check(TokenType::Tilde) {
21058                stage_path.push('~');
21059                self.skip(); // consume ~
21060                                // After @~, parse any path segments
21061                while self.check(TokenType::Slash) {
21062                    self.skip(); // consume /
21063                    stage_path.push('/');
21064                    if (self.check(TokenType::Var)
21065                        || self.check_keyword()
21066                        || self.is_identifier_token())
21067                        && !self.check_next(TokenType::Eq)
21068                    {
21069                        stage_path.push_str(&self.advance().text);
21070                    }
21071                }
21072                return Ok(Expression::Literal(Literal::String(stage_path)));
21073            }
21074
21075            // Get stage name (could be quoted identifier)
21076            if self.check(TokenType::QuotedIdentifier) {
21077                // Preserve quoted identifier with quotes
21078                let text = &self.peek().text;
21079                stage_path.push('"');
21080                stage_path.push_str(text);
21081                stage_path.push('"');
21082                self.skip();
21083            } else if self.check(TokenType::Var)
21084                || self.check_keyword()
21085                || self.check(TokenType::Identifier)
21086            {
21087                stage_path.push_str(&self.advance().text);
21088            }
21089
21090            // Parse qualified name parts: .schema.stage (may include quoted identifiers)
21091            while self.check(TokenType::Dot) {
21092                self.skip(); // consume .
21093                stage_path.push('.');
21094                if self.check(TokenType::QuotedIdentifier) {
21095                    // Preserve quoted identifier with quotes
21096                    let text = &self.peek().text;
21097                    stage_path.push('"');
21098                    stage_path.push_str(text);
21099                    stage_path.push('"');
21100                    self.skip();
21101                } else if self.check(TokenType::Var)
21102                    || self.check_keyword()
21103                    || self.check(TokenType::Identifier)
21104                {
21105                    stage_path.push_str(&self.advance().text);
21106                }
21107            }
21108
21109            // Parse path segments: /path/to/file
21110            while self.check(TokenType::Slash) {
21111                self.skip(); // consume /
21112                stage_path.push('/');
21113                // Get path segment but don't consume if followed by = (that's a parameter)
21114                if (self.check(TokenType::Var)
21115                    || self.check_keyword()
21116                    || self.is_identifier_token())
21117                    && !self.check_next(TokenType::Eq)
21118                {
21119                    stage_path.push_str(&self.advance().text);
21120                }
21121            }
21122            return Ok(Expression::Literal(Literal::String(stage_path)));
21123        }
21124
21125        // Stage reference tokenized as a Var starting with @ (e.g., @s1)
21126        if self.check(TokenType::Var) && self.peek().text.starts_with('@') {
21127            let mut stage_path = self.advance().text.clone();
21128
21129            // Parse qualified name parts: .schema.stage (may include quoted identifiers)
21130            while self.check(TokenType::Dot) {
21131                self.skip(); // consume .
21132                stage_path.push('.');
21133                if self.check(TokenType::QuotedIdentifier) {
21134                    let text = &self.peek().text;
21135                    stage_path.push('"');
21136                    stage_path.push_str(text);
21137                    stage_path.push('"');
21138                    self.skip();
21139                } else if self.check(TokenType::Var)
21140                    || self.check_keyword()
21141                    || self.check(TokenType::Identifier)
21142                {
21143                    stage_path.push_str(&self.advance().text);
21144                }
21145            }
21146
21147            // Parse path segments: /path/to/file
21148            while self.check(TokenType::Slash) {
21149                self.skip(); // consume /
21150                stage_path.push('/');
21151                if (self.check(TokenType::Var)
21152                    || self.check_keyword()
21153                    || self.is_identifier_token())
21154                    && !self.check_next(TokenType::Eq)
21155                {
21156                    stage_path.push_str(&self.advance().text);
21157                }
21158            }
21159            return Ok(Expression::Literal(Literal::String(stage_path)));
21160        }
21161
21162        Err(self.parse_error("Expected stage reference starting with @"))
21163    }
21164
21165    /// Parse PUT statement (Snowflake)
21166    /// PUT file://<path> @<stage> [AUTO_COMPRESS = TRUE|FALSE] ...
21167    fn parse_put(&mut self) -> Result<Expression> {
21168        self.expect(TokenType::Put)?;
21169
21170        // Parse source file path (usually file:///path/to/file)
21171        let (source, source_quoted) = if self.check(TokenType::String) {
21172            (self.advance().text.clone(), true)
21173        } else {
21174            // Handle file://path syntax (parsed as identifier + colon + etc.)
21175            // Stop when we see @ (start of stage reference)
21176            let mut source_parts = Vec::new();
21177            while !self.is_at_end() {
21178                // Stop if we see @ (DAt token or Var starting with @)
21179                if self.check(TokenType::DAt) {
21180                    break;
21181                }
21182                if self.check(TokenType::Var) && self.peek().text.starts_with('@') {
21183                    break;
21184                }
21185                let token = self.advance();
21186                source_parts.push(token.text.clone());
21187            }
21188            (source_parts.join(""), false)
21189        };
21190
21191        // Parse target stage (@stage_name)
21192        let target = self.parse_stage_reference_as_string()?;
21193
21194        // Parse optional parameters
21195        // Note: Some parameter names like OVERWRITE are keywords, so we check for those explicitly
21196        // Preserve original casing for identity tests
21197        let mut params = Vec::new();
21198        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
21199            let is_param_name = self.check(TokenType::Var)
21200                || self.check_keyword()
21201                || self.check(TokenType::Overwrite);
21202            if is_param_name {
21203                let name = self.advance().text.clone();
21204                let value = if self.match_token(TokenType::Eq) {
21205                    Some(self.parse_primary()?)
21206                } else {
21207                    None
21208                };
21209                params.push(CopyParameter {
21210                    name,
21211                    value,
21212                    values: Vec::new(),
21213                    eq: true,
21214                });
21215            } else {
21216                break;
21217            }
21218        }
21219
21220        Ok(Expression::Put(Box::new(PutStmt {
21221            source,
21222            source_quoted,
21223            target,
21224            params,
21225        })))
21226    }
21227
21228    /// Helper to join command tokens with smart spacing
21229    /// Preserves the structure of file paths, stage references, etc.
21230    fn join_command_tokens(&self, tokens: Vec<(String, TokenType)>) -> String {
21231        let mut result = String::new();
21232        let mut prev_token_type: Option<TokenType> = None;
21233        let mut prev_prev_token_type: Option<TokenType> = None;
21234
21235        for (i, (text, token_type)) in tokens.iter().enumerate() {
21236            let needs_space = if result.is_empty() {
21237                false
21238            } else {
21239                match (prev_token_type, *token_type) {
21240                    // No space after @ (stage references: @stage, @%, @~)
21241                    (Some(TokenType::DAt), _) => false,
21242                    // No space around dots (identifiers: a.b.c)
21243                    (Some(TokenType::Dot), _) => false,
21244                    (_, TokenType::Dot) => false,
21245                    // No space around parentheses
21246                    (Some(TokenType::LParen), _) => false,
21247                    (_, TokenType::LParen) => false,
21248                    (_, TokenType::RParen) => false,
21249                    // No space around square brackets (array access: arr[i])
21250                    (Some(TokenType::LBracket), _) => false,
21251                    (_, TokenType::LBracket) => false,
21252                    (_, TokenType::RBracket) => false,
21253                    // No space before ,
21254                    (_, TokenType::Comma) => false,
21255                    // No space around / (paths: @s1/test)
21256                    (Some(TokenType::Slash), _) => false,
21257                    (_, TokenType::Slash) => false,
21258                    // No space around : (file://path)
21259                    (Some(TokenType::Colon), _) => false,
21260                    (_, TokenType::Colon) => false,
21261                    // No space around % (table stage: @%table)
21262                    (Some(TokenType::Mod), _) => false,
21263                    (_, TokenType::Mod) => false,
21264                    (Some(TokenType::Percent), _) => false,
21265                    (_, TokenType::Percent) => false,
21266                    // Handle = contextually:
21267                    // - No space around = in simple KEY=VALUE patterns where value is terminal
21268                    //   (PARALLEL=1, ENABLED=TRUE, FILE_FORMAT='csv')
21269                    // - Keep space for expressions like SET x = x + 1
21270                    (Some(TokenType::Var), TokenType::Eq) => {
21271                        // If the var starts with @ (parameter like @id = 123), always use spaces
21272                        if i >= 1 && tokens[i - 1].0.starts_with('@') {
21273                            true
21274                        } else if i + 1 < tokens.len() {
21275                            // Check what follows: Var=Number where number is terminal (end or followed by Var)
21276                            let next_type = tokens[i + 1].1;
21277                            // Is the value terminal (end of tokens, or followed by another Var=... pattern)?
21278                            let is_terminal_value =
21279                                i + 2 >= tokens.len() || tokens[i + 2].1 == TokenType::Var;
21280                            match next_type {
21281                                // No space for terminal numbers/bools: PARALLEL=1, ENABLED=TRUE
21282                                // Return false (no space) when terminal
21283                                TokenType::Number | TokenType::True | TokenType::False => {
21284                                    !is_terminal_value
21285                                }
21286                                // No space for terminal strings: FILE_FORMAT='csv'
21287                                TokenType::String => !is_terminal_value,
21288                                // Always space if followed by Var (SET x = y ...)
21289                                _ => true,
21290                            }
21291                        } else {
21292                            true
21293                        }
21294                    }
21295                    // No space after = in terminal KEY=VALUE patterns
21296                    (Some(TokenType::Eq), TokenType::Number)
21297                    | (Some(TokenType::Eq), TokenType::True)
21298                    | (Some(TokenType::Eq), TokenType::False)
21299                    | (Some(TokenType::Eq), TokenType::String) => {
21300                        // Is this a terminal value (end or followed by another Var=...)?
21301                        let is_terminal =
21302                            i + 1 >= tokens.len() || tokens[i + 1].1 == TokenType::Var;
21303                        match prev_prev_token_type {
21304                            // No space (return false) when terminal, space otherwise
21305                            // But always space if the var before = was preceded by @ (parameter)
21306                            Some(TokenType::Var) => {
21307                                // Always space if the var before = starts with @ (parameter)
21308                                if i >= 2 && tokens[i - 2].0.starts_with('@') {
21309                                    true
21310                                } else {
21311                                    !is_terminal
21312                                }
21313                            }
21314                            _ => true, // Space for other cases
21315                        }
21316                    }
21317                    // Always space after = when followed by Var (SET x = y, could be expression)
21318                    (Some(TokenType::Eq), TokenType::Var) => true,
21319                    // No space around :: (cast)
21320                    (Some(TokenType::DColon), _) => false,
21321                    (_, TokenType::DColon) => false,
21322                    // Default: add space
21323                    _ => true,
21324                }
21325            };
21326
21327            if needs_space {
21328                result.push(' ');
21329            }
21330            result.push_str(text);
21331            prev_prev_token_type = prev_token_type;
21332            prev_token_type = Some(*token_type);
21333        }
21334        result
21335    }
21336
21337    /// Join Teradata table option tokens with Teradata-specific spacing
21338    /// - No spaces around '='
21339    /// - No spaces around dots or parentheses
21340    /// - Space-separated words otherwise
21341    fn join_teradata_option_tokens(&self, tokens: Vec<(String, TokenType)>) -> String {
21342        let mut result = String::new();
21343        let mut prev_token_type: Option<TokenType> = None;
21344
21345        for (text, token_type) in tokens {
21346            let needs_space = if result.is_empty() {
21347                false
21348            } else {
21349                match (prev_token_type, token_type) {
21350                    (Some(TokenType::Dot), _) => false,
21351                    (_, TokenType::Dot) => false,
21352                    (Some(TokenType::LParen), _) => false,
21353                    (_, TokenType::LParen) => false,
21354                    (_, TokenType::RParen) => false,
21355                    (_, TokenType::Comma) => false,
21356                    (Some(TokenType::Eq), _) => false,
21357                    (_, TokenType::Eq) => false,
21358                    _ => true,
21359                }
21360            };
21361
21362            if needs_space {
21363                result.push(' ');
21364            }
21365            result.push_str(&text);
21366            prev_token_type = Some(token_type);
21367        }
21368
21369        result
21370    }
21371
21372    /// Parse RM or REMOVE command (Snowflake)
21373    /// RM @stage_name / REMOVE @stage_name
21374    fn parse_rm_command(&mut self) -> Result<Expression> {
21375        let command_token = self.advance(); // RM or REMOVE
21376        let command_name = command_token.text.to_ascii_uppercase();
21377
21378        // Collect remaining tokens with their types
21379        let mut tokens = vec![(command_name, command_token.token_type)];
21380        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
21381            let token = self.advance();
21382            tokens.push((token.text.clone(), token.token_type));
21383        }
21384
21385        Ok(Expression::Command(Box::new(Command {
21386            this: self.join_command_tokens(tokens),
21387        })))
21388    }
21389
21390    /// Parse GET command (Snowflake)
21391    /// GET @stage_name 'file:///path'
21392    fn parse_get_command(&mut self) -> Result<Expression> {
21393        let get_token = self.advance(); // consume GET (it's already matched)
21394
21395        // Collect remaining tokens with their types, preserving quotes
21396        let mut tokens = vec![("GET".to_string(), get_token.token_type)];
21397        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
21398            let token = self.advance();
21399            // Re-add quotes around string and quoted identifier tokens
21400            let text = match token.token_type {
21401                TokenType::String => format!("'{}'", token.text),
21402                TokenType::QuotedIdentifier => format!("\"{}\"", token.text),
21403                _ => token.text.clone(),
21404            };
21405            tokens.push((text, token.token_type));
21406        }
21407
21408        Ok(Expression::Command(Box::new(Command {
21409            this: self.join_command_tokens(tokens),
21410        })))
21411    }
21412
21413    /// Parse CALL statement (stored procedure call)
21414    /// CALL procedure_name(args, ...)
21415    fn parse_call(&mut self) -> Result<Expression> {
21416        let call_token = self.advance(); // consume CALL
21417
21418        // Collect remaining tokens with their types
21419        let mut tokens = vec![("CALL".to_string(), call_token.token_type)];
21420        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
21421            let token = self.advance();
21422            tokens.push((token.text.clone(), token.token_type));
21423        }
21424
21425        Ok(Expression::Command(Box::new(Command {
21426            this: self.join_command_tokens(tokens),
21427        })))
21428    }
21429
21430    /// Parse KILL statement (MySQL/MariaDB)
21431    /// KILL [CONNECTION | QUERY] <id>
21432    fn parse_kill(&mut self) -> Result<Expression> {
21433        self.expect(TokenType::Kill)?;
21434
21435        // Check for optional kind: CONNECTION or QUERY
21436        let kind = if self.match_identifier("CONNECTION") {
21437            Some("CONNECTION".to_string())
21438        } else if self.match_identifier("QUERY") {
21439            Some("QUERY".to_string())
21440        } else {
21441            None
21442        };
21443
21444        // Parse the target (process ID - usually a number or string)
21445        let this = self.parse_primary()?;
21446
21447        Ok(Expression::Kill(Box::new(Kill { this, kind })))
21448    }
21449
21450    /// Parse EXEC/EXECUTE statement (TSQL stored procedure call)
21451    /// EXEC [schema.]procedure_name [@param=value, ...]
21452    fn parse_execute(&mut self) -> Result<Expression> {
21453        self.expect(TokenType::Execute)?;
21454
21455        // Parse procedure name (can be qualified: schema.proc_name)
21456        let proc_name = self.parse_table_ref()?;
21457        let this = Expression::Table(Box::new(proc_name));
21458
21459        // Parse optional parameters: @param=value, ...
21460        let mut parameters = Vec::new();
21461
21462        // Check if there are parameters (starts with @ or identifier)
21463        while self.check(TokenType::Var) || self.check(TokenType::Parameter) {
21464            // Get the parameter name (starts with @)
21465            let token = self.advance();
21466            let param_name = if token.text.starts_with('@') {
21467                token.text.clone()
21468            } else {
21469                format!("@{}", token.text)
21470            };
21471
21472            // Check for = (named parameter) or positional parameter
21473            if self.match_token(TokenType::Eq) {
21474                // Named parameter: @param = value
21475                let value = self.parse_primary()?;
21476                parameters.push(ExecuteParameter {
21477                    name: param_name,
21478                    value,
21479                    positional: false,
21480                });
21481            } else {
21482                // Positional parameter: @var (no = sign)
21483                // Positional parameter: @var (no = sign)
21484                parameters.push(ExecuteParameter {
21485                    name: param_name.clone(),
21486                    value: Expression::boxed_column(Column {
21487                        name: Identifier::new(&param_name),
21488                        table: None,
21489                        join_mark: false,
21490                        trailing_comments: Vec::new(),
21491                        span: None,
21492                        inferred_type: None,
21493                    }),
21494                    positional: true,
21495                });
21496            }
21497
21498            // Check for comma to continue
21499            if !self.match_token(TokenType::Comma) {
21500                break;
21501            }
21502        }
21503
21504        Ok(Expression::Execute(Box::new(ExecuteStatement {
21505            this,
21506            parameters,
21507        })))
21508    }
21509
21510    /// Parse GRANT statement
21511    /// GRANT <privileges> ON [<kind>] <object> TO <principals> [WITH GRANT OPTION]
21512    fn parse_grant(&mut self) -> Result<Expression> {
21513        self.expect(TokenType::Grant)?;
21514
21515        // ClickHouse: GRANT can grant roles (no ON clause), grant privileges (has ON clause),
21516        // or use complex syntax. If we see TO before ON, treat as command.
21517        // Also: multi-privilege grants (multiple ON), wildcard grants (test*.*),
21518        // WITH REPLACE OPTION all parse as commands.
21519        if matches!(
21520            self.config.dialect,
21521            Some(crate::dialects::DialectType::ClickHouse)
21522        ) {
21523            // Save position after GRANT keyword
21524            let saved_pos = self.current;
21525            // Scan ahead to check grant structure
21526            let mut depth = 0i32;
21527            let mut on_count = 0;
21528            let mut found_to = false;
21529            let mut has_star_in_name = false;
21530            let mut has_replace_option = false;
21531            let mut i = self.current;
21532            while i < self.tokens.len() && self.tokens[i].token_type != TokenType::Semicolon {
21533                match self.tokens[i].token_type {
21534                    TokenType::LParen => depth += 1,
21535                    TokenType::RParen => depth -= 1,
21536                    TokenType::On if depth == 0 => on_count += 1,
21537                    TokenType::To if depth == 0 => {
21538                        found_to = true;
21539                    }
21540                    TokenType::Star if depth == 0 && on_count > 0 && !found_to => {
21541                        // Check if star is part of a wildcard name (e.g., test*.*)
21542                        if i > 0
21543                            && self.tokens[i - 1].token_type != TokenType::Dot
21544                            && self.tokens[i - 1].token_type != TokenType::On
21545                        {
21546                            has_star_in_name = true;
21547                        }
21548                    }
21549                    TokenType::Replace if depth == 0 && found_to => {
21550                        has_replace_option = true;
21551                    }
21552                    _ => {}
21553                }
21554                i += 1;
21555            }
21556            if (found_to && on_count == 0) || on_count > 1 || has_star_in_name || has_replace_option
21557            {
21558                // Role grant, multi-privilege grant, wildcard grant, or REPLACE OPTION — parse as command
21559                self.current = saved_pos;
21560                return self
21561                    .parse_command()?
21562                    .ok_or_else(|| self.parse_error("Failed to parse GRANT statement"));
21563            }
21564            self.current = saved_pos;
21565        }
21566
21567        // Parse privileges (e.g., SELECT, INSERT, UPDATE)
21568        let privileges = self.parse_privileges()?;
21569
21570        // Expect ON
21571        self.expect(TokenType::On)?;
21572
21573        // Parse optional kind (TABLE, SCHEMA, FUNCTION, etc.)
21574        let kind = self.parse_object_kind()?;
21575
21576        // Parse securable (the object) - may be dot-separated qualified name
21577        let securable = self.parse_securable_name()?;
21578
21579        // Parse optional function parameter types: func(type1, type2, ...)
21580        let function_params = if self.check(TokenType::LParen) {
21581            self.parse_function_param_types()?
21582        } else {
21583            Vec::new()
21584        };
21585
21586        // Expect TO
21587        self.expect(TokenType::To)?;
21588
21589        // Parse principals
21590        let principals = self.parse_principals()?;
21591
21592        // Check for WITH GRANT OPTION
21593        let grant_option = self.match_token(TokenType::With)
21594            && self.check(TokenType::Grant)
21595            && {
21596                self.skip();
21597                self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("OPTION")
21598            }
21599            && {
21600                self.skip();
21601                true
21602            };
21603
21604        // Check for TSQL AS principal clause
21605        let as_principal = if self.match_token(TokenType::As) {
21606            let name = self.expect_identifier_or_keyword()?;
21607            Some(Identifier::new(name))
21608        } else {
21609            None
21610        };
21611
21612        Ok(Expression::Grant(Box::new(Grant {
21613            privileges,
21614            kind,
21615            securable,
21616            function_params,
21617            principals,
21618            grant_option,
21619            as_principal,
21620        })))
21621    }
21622
21623    /// Parse REVOKE statement
21624    /// REVOKE [GRANT OPTION FOR] <privileges> ON [<kind>] <object> FROM <principals> [CASCADE]
21625    fn parse_revoke(&mut self) -> Result<Expression> {
21626        self.expect(TokenType::Revoke)?;
21627
21628        // ClickHouse: REVOKE role FROM user (no ON clause), multi-privilege, or wildcard — parse as command
21629        if matches!(
21630            self.config.dialect,
21631            Some(crate::dialects::DialectType::ClickHouse)
21632        ) {
21633            let saved_pos = self.current;
21634            let mut depth = 0i32;
21635            let mut on_count = 0;
21636            let mut found_from = false;
21637            let mut has_star_in_name = false;
21638            let mut i = self.current;
21639            while i < self.tokens.len() && self.tokens[i].token_type != TokenType::Semicolon {
21640                match self.tokens[i].token_type {
21641                    TokenType::LParen => depth += 1,
21642                    TokenType::RParen => depth -= 1,
21643                    TokenType::On if depth == 0 => on_count += 1,
21644                    TokenType::From if depth == 0 => {
21645                        found_from = true;
21646                    }
21647                    TokenType::Star if depth == 0 && on_count > 0 && !found_from => {
21648                        if i > 0
21649                            && self.tokens[i - 1].token_type != TokenType::Dot
21650                            && self.tokens[i - 1].token_type != TokenType::On
21651                        {
21652                            has_star_in_name = true;
21653                        }
21654                    }
21655                    _ => {}
21656                }
21657                i += 1;
21658            }
21659            if (found_from && on_count == 0) || on_count > 1 || has_star_in_name {
21660                self.current = saved_pos;
21661                return self
21662                    .parse_command()?
21663                    .ok_or_else(|| self.parse_error("Failed to parse REVOKE statement"));
21664            }
21665            self.current = saved_pos;
21666        }
21667
21668        // Check for GRANT OPTION FOR
21669        let grant_option = if self.check(TokenType::Grant) {
21670            self.skip();
21671            if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("OPTION") {
21672                self.skip();
21673                self.expect(TokenType::For)?;
21674                true
21675            } else {
21676                return Err(self.parse_error("Expected OPTION after GRANT in REVOKE"));
21677            }
21678        } else {
21679            false
21680        };
21681
21682        // Parse privileges
21683        let privileges = self.parse_privileges()?;
21684
21685        // Expect ON
21686        self.expect(TokenType::On)?;
21687
21688        // Parse optional kind
21689        let kind = self.parse_object_kind()?;
21690
21691        // Parse securable - may be dot-separated qualified name
21692        let securable = self.parse_securable_name()?;
21693
21694        // Parse optional function parameter types: func(type1, type2, ...)
21695        let function_params = if self.check(TokenType::LParen) {
21696            self.parse_function_param_types()?
21697        } else {
21698            Vec::new()
21699        };
21700
21701        // Expect FROM
21702        self.expect(TokenType::From)?;
21703
21704        // Parse principals
21705        let principals = self.parse_principals()?;
21706
21707        // Check for CASCADE or RESTRICT
21708        let cascade = self.match_token(TokenType::Cascade);
21709        let restrict = if !cascade {
21710            self.match_token(TokenType::Restrict)
21711        } else {
21712            false
21713        };
21714
21715        Ok(Expression::Revoke(Box::new(Revoke {
21716            privileges,
21717            kind,
21718            securable,
21719            function_params,
21720            principals,
21721            grant_option,
21722            cascade,
21723            restrict,
21724        })))
21725    }
21726
21727    /// Parse privilege list for GRANT/REVOKE
21728    /// Handles multi-word privileges like "ALL PRIVILEGES" and column-level privileges like "SELECT(col1, col2)"
21729    fn parse_privileges(&mut self) -> Result<Vec<Privilege>> {
21730        let mut privileges = Vec::new();
21731        loop {
21732            let mut priv_parts = Vec::new();
21733            // Collect privilege words until we hit ON, comma, LParen, or similar terminator
21734            while !self.is_at_end() {
21735                if self.check(TokenType::On)
21736                    || self.check(TokenType::Comma)
21737                    || self.check(TokenType::LParen)
21738                {
21739                    break;
21740                }
21741                if self.is_identifier_or_keyword_token() {
21742                    priv_parts.push(self.advance().text.to_ascii_uppercase());
21743                } else {
21744                    break;
21745                }
21746            }
21747            if priv_parts.is_empty() {
21748                break;
21749            }
21750            let priv_name = priv_parts.join(" ");
21751
21752            // Check for column list in parentheses: SELECT(col1, col2)
21753            let columns = if self.match_token(TokenType::LParen) {
21754                let mut cols = Vec::new();
21755                loop {
21756                    // Parse column name (identifier)
21757                    if self.is_identifier_or_keyword_token() {
21758                        cols.push(self.advance().text.to_string());
21759                    } else if self.check(TokenType::RParen) {
21760                        break;
21761                    } else {
21762                        break;
21763                    }
21764                    if !self.match_token(TokenType::Comma) {
21765                        break;
21766                    }
21767                }
21768                self.expect(TokenType::RParen)?;
21769                cols
21770            } else {
21771                Vec::new()
21772            };
21773
21774            privileges.push(Privilege {
21775                name: priv_name,
21776                columns,
21777            });
21778            if !self.match_token(TokenType::Comma) {
21779                break;
21780            }
21781        }
21782        Ok(privileges)
21783    }
21784
21785    /// Parse object kind (TABLE, SCHEMA, FUNCTION, PROCEDURE, SEQUENCE, etc.)
21786    fn parse_object_kind(&mut self) -> Result<Option<String>> {
21787        if self.check(TokenType::Table) {
21788            self.skip();
21789            Ok(Some("TABLE".to_string()))
21790        } else if self.check(TokenType::Schema) {
21791            self.skip();
21792            Ok(Some("SCHEMA".to_string()))
21793        } else if self.check(TokenType::Database) {
21794            self.skip();
21795            Ok(Some("DATABASE".to_string()))
21796        } else if self.check(TokenType::Function) {
21797            self.skip();
21798            Ok(Some("FUNCTION".to_string()))
21799        } else if self.check(TokenType::View) {
21800            self.skip();
21801            Ok(Some("VIEW".to_string()))
21802        } else if self.check(TokenType::Procedure) {
21803            self.skip();
21804            Ok(Some("PROCEDURE".to_string()))
21805        } else if self.check(TokenType::Sequence) {
21806            self.skip();
21807            Ok(Some("SEQUENCE".to_string()))
21808        } else {
21809            Ok(None)
21810        }
21811    }
21812
21813    /// Parse principal list for GRANT/REVOKE
21814    fn parse_principals(&mut self) -> Result<Vec<GrantPrincipal>> {
21815        let mut principals = Vec::new();
21816        loop {
21817            // Check for ROLE keyword (TokenType::Var with text "ROLE")
21818            let is_role = if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("ROLE")
21819            {
21820                self.skip();
21821                true
21822            } else {
21823                false
21824            };
21825            // Check for GROUP keyword (Redshift) - TokenType::Group
21826            let is_group = if !is_role && self.check(TokenType::Group) {
21827                self.skip();
21828                true
21829            } else {
21830                false
21831            };
21832            // Parse principal name (with quoted flag preserved for backtick-quoted identifiers)
21833            let name = self.expect_identifier_or_keyword_with_quoted()?;
21834            principals.push(GrantPrincipal {
21835                name,
21836                is_role,
21837                is_group,
21838            });
21839            if !self.match_token(TokenType::Comma) {
21840                break;
21841            }
21842        }
21843        Ok(principals)
21844    }
21845
21846    /// Parse a securable name (potentially dot-separated qualified name)
21847    /// e.g., "mydb.myschema.ADD5" -> Identifier("mydb.myschema.ADD5")
21848    fn parse_securable_name(&mut self) -> Result<Identifier> {
21849        // Accept * as a name part (e.g., GRANT ON *.* or GRANT ON db.*)
21850        let first = if self.match_token(TokenType::Star) {
21851            "*".to_string()
21852        } else {
21853            self.expect_identifier_or_keyword()?
21854        };
21855        let mut parts = vec![first];
21856
21857        while self.match_token(TokenType::Dot) {
21858            let next = if self.match_token(TokenType::Star) {
21859                "*".to_string()
21860            } else {
21861                self.expect_identifier_or_keyword()?
21862            };
21863            parts.push(next);
21864        }
21865
21866        Ok(Identifier::new(parts.join(".")))
21867    }
21868
21869    /// Parse function parameter types for GRANT/REVOKE ON FUNCTION
21870    /// e.g., "(number, varchar)" -> vec!["number", "varchar"]
21871    fn parse_function_param_types(&mut self) -> Result<Vec<String>> {
21872        self.expect(TokenType::LParen)?;
21873
21874        let mut params = Vec::new();
21875        if !self.check(TokenType::RParen) {
21876            loop {
21877                // Parse parameter type - can be a keyword (INT, VARCHAR) or identifier
21878                let param_type = self.expect_identifier_or_keyword()?;
21879                params.push(param_type);
21880                if !self.match_token(TokenType::Comma) {
21881                    break;
21882                }
21883            }
21884        }
21885
21886        self.expect(TokenType::RParen)?;
21887        Ok(params)
21888    }
21889
21890    /// Parse COMMENT ON statement
21891    fn parse_comment(&mut self) -> Result<Expression> {
21892        self.expect(TokenType::Comment)?;
21893
21894        // Check for IF EXISTS
21895        let exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
21896
21897        // Expect ON
21898        self.expect(TokenType::On)?;
21899
21900        // Check for MATERIALIZED (can be TokenType::Materialized or TokenType::Var)
21901        let materialized = if self.match_token(TokenType::Materialized) {
21902            true
21903        } else if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("MATERIALIZED") {
21904            self.skip();
21905            true
21906        } else {
21907            false
21908        };
21909
21910        // Parse the object kind (COLUMN, TABLE, DATABASE, PROCEDURE, etc.)
21911        let kind = self.expect_identifier_or_keyword()?.to_ascii_uppercase();
21912
21913        // Parse the object name (can be qualified like schema.table.column)
21914        // For PROCEDURE/FUNCTION, we need to handle the parameter list like my_proc(integer, integer)
21915        let this = if kind == "PROCEDURE" || kind == "FUNCTION" {
21916            // Parse name possibly with parameter types, preserving original case
21917            let name_token = self.advance();
21918            let mut name_str = name_token.text.clone();
21919
21920            // Parse additional qualified parts
21921            while self.match_token(TokenType::Dot) {
21922                let next = self.advance();
21923                name_str.push('.');
21924                name_str.push_str(&next.text);
21925            }
21926
21927            // Check for parameter types in parentheses
21928            if self.match_token(TokenType::LParen) {
21929                name_str.push('(');
21930                let mut first = true;
21931                while !self.check(TokenType::RParen) && !self.is_at_end() {
21932                    if !first {
21933                        name_str.push_str(", ");
21934                    }
21935                    first = false;
21936                    let param_token = self.advance();
21937                    name_str.push_str(&param_token.text);
21938                    self.match_token(TokenType::Comma);
21939                }
21940                self.expect(TokenType::RParen)?;
21941                name_str.push(')');
21942            }
21943
21944            Expression::Identifier(Identifier::new(name_str))
21945        } else {
21946            self.parse_qualified_name()?
21947        };
21948
21949        // Expect IS
21950        if self.check(TokenType::Is) {
21951            self.skip();
21952        } else {
21953            return Err(self.parse_error("Expected IS in COMMENT ON statement"));
21954        }
21955
21956        // Parse the comment expression (usually a string literal)
21957        let expression = self.parse_primary()?;
21958
21959        Ok(Expression::Comment(Box::new(Comment {
21960            this,
21961            kind,
21962            expression,
21963            exists,
21964            materialized,
21965        })))
21966    }
21967
21968    /// Parse SET statement
21969    fn parse_set(&mut self) -> Result<Expression> {
21970        self.expect(TokenType::Set)?;
21971
21972        let mut items = Vec::new();
21973
21974        // ClickHouse: SET DEFAULT ROLE ... TO user - parse as command
21975        if matches!(
21976            self.config.dialect,
21977            Some(crate::dialects::DialectType::ClickHouse)
21978        ) && self.check(TokenType::Default)
21979        {
21980            let mut parts = vec!["SET".to_string()];
21981            while !self.is_at_end() && self.peek().token_type != TokenType::Semicolon {
21982                parts.push(self.advance().text.clone());
21983            }
21984            return Ok(Expression::Command(Box::new(crate::expressions::Command {
21985                this: parts.join(" "),
21986            })));
21987        }
21988
21989        // Teradata: SET QUERY_BAND = ... [UPDATE] [FOR scope]
21990        if matches!(
21991            self.config.dialect,
21992            Some(crate::dialects::DialectType::Teradata)
21993        ) && self.match_identifier("QUERY_BAND")
21994        {
21995            return self.parse_query_band();
21996        }
21997
21998        // Handle MySQL SET CHARACTER SET / SET NAMES
21999        if self.match_identifier("CHARACTER") {
22000            // SET CHARACTER SET <charset> | SET CHARACTER SET DEFAULT
22001            self.expect(TokenType::Set)?;
22002            let value = if self.match_token(TokenType::Default) {
22003                Expression::Identifier(Identifier::new("DEFAULT".to_string()))
22004            } else {
22005                self.parse_primary()?
22006            };
22007            items.push(SetItem {
22008                name: Expression::Identifier(Identifier::new("CHARACTER SET".to_string())),
22009                value,
22010                kind: None,
22011                no_equals: false,
22012            });
22013            return Ok(Expression::SetStatement(Box::new(SetStatement { items })));
22014        }
22015
22016        if self.match_identifier("NAMES") {
22017            // SET NAMES <charset> [COLLATE <collation>] | SET NAMES DEFAULT
22018            let value = if self.match_token(TokenType::Default) {
22019                Expression::Identifier(Identifier::new("DEFAULT".to_string()))
22020            } else {
22021                self.parse_primary()?
22022            };
22023            // Check for optional COLLATE clause
22024            let collation = if self.match_identifier("COLLATE") {
22025                Some(self.parse_primary()?)
22026            } else {
22027                None
22028            };
22029            items.push(SetItem {
22030                name: Expression::Identifier(Identifier::new("NAMES".to_string())),
22031                value,
22032                kind: None,
22033                no_equals: false,
22034            });
22035            if let Some(coll) = collation {
22036                items.push(SetItem {
22037                    name: Expression::Identifier(Identifier::new("COLLATE".to_string())),
22038                    value: coll,
22039                    kind: None,
22040                    no_equals: false,
22041                });
22042            }
22043            return Ok(Expression::SetStatement(Box::new(SetStatement { items })));
22044        }
22045
22046        loop {
22047            // Check for GLOBAL, LOCAL, SESSION, PERSIST, PERSIST_ONLY modifiers
22048            // LOCAL is a token type, others are identifiers
22049            let kind = if self.match_identifier("GLOBAL") {
22050                Some("GLOBAL".to_string())
22051            } else if self.match_token(TokenType::Local) {
22052                Some("LOCAL".to_string())
22053            } else if self.match_identifier("SESSION") {
22054                Some("SESSION".to_string())
22055            } else if self.match_identifier("PERSIST") {
22056                Some("PERSIST".to_string())
22057            } else if self.match_identifier("PERSIST_ONLY") {
22058                Some("PERSIST_ONLY".to_string())
22059            } else {
22060                None
22061            };
22062
22063            // Check for SET [GLOBAL|SESSION] TRANSACTION (MySQL)
22064            if self.match_token(TokenType::Transaction) {
22065                // Parse transaction characteristics (ISOLATION LEVEL, READ ONLY, READ WRITE)
22066                let mut characteristics = Vec::new();
22067                loop {
22068                    let mut char_tokens = Vec::new();
22069                    // Parse ISOLATION LEVEL ... or READ ONLY/WRITE
22070                    // Must handle keywords like ONLY, REPEATABLE, SERIALIZABLE, etc.
22071                    while !self.is_at_end()
22072                        && !self.check(TokenType::Comma)
22073                        && !self.check(TokenType::Semicolon)
22074                    {
22075                        // Allow identifiers and common transaction-related keywords
22076                        if self.is_identifier_token()
22077                            || self.is_safe_keyword_as_identifier()
22078                            || self.check(TokenType::Only)
22079                            || self.check(TokenType::Repeatable)
22080                        {
22081                            char_tokens.push(self.advance().text);
22082                        } else {
22083                            break;
22084                        }
22085                    }
22086                    if !char_tokens.is_empty() {
22087                        characteristics.push(char_tokens.join(" "));
22088                    }
22089                    if !self.match_token(TokenType::Comma) {
22090                        break;
22091                    }
22092                }
22093
22094                let name = Expression::Identifier(Identifier::new("TRANSACTION".to_string()));
22095                let value = if characteristics.is_empty() {
22096                    Expression::Identifier(Identifier::new("".to_string()))
22097                } else {
22098                    Expression::Identifier(Identifier::new(characteristics.join(", ")))
22099                };
22100
22101                items.push(SetItem {
22102                    name,
22103                    value,
22104                    kind,
22105                    no_equals: false,
22106                });
22107                break;
22108            }
22109
22110            // Handle DuckDB: SET VARIABLE var = value
22111            // Only match if "VARIABLE" is followed by another identifier (not by = or TO)
22112            let is_variable = if self.check(TokenType::Var)
22113                && self.peek().text.eq_ignore_ascii_case("VARIABLE")
22114            {
22115                // Look ahead: VARIABLE should be followed by another name, not by = or TO
22116                if let Some(next) = self.tokens.get(self.current + 1) {
22117                    if next.token_type != TokenType::Eq
22118                        && next.token_type != TokenType::To
22119                        && next.token_type != TokenType::ColonEq
22120                    {
22121                        self.skip(); // consume VARIABLE
22122                        true
22123                    } else {
22124                        false
22125                    }
22126                } else {
22127                    false
22128                }
22129            } else {
22130                false
22131            };
22132
22133            // Parse variable name - use a simple approach to avoid expression parsing issues
22134            // Variable names can be dotted identifiers or keywords used as names
22135            let name = {
22136                if self.check(TokenType::AtAt) {
22137                    // @@SCOPE.variable or @@variable syntax (MySQL system variables)
22138                    self.skip(); // consume @@
22139                    let mut name_str = "@@".to_string();
22140                    let first = self.advance().text.clone();
22141                    name_str.push_str(&first);
22142                    // Handle @@scope.variable (e.g., @@GLOBAL.max_connections)
22143                    while self.match_token(TokenType::Dot) {
22144                        let next = self.advance().text.clone();
22145                        name_str.push('.');
22146                        name_str.push_str(&next);
22147                    }
22148                    Expression::Identifier(Identifier::new(name_str))
22149                } else if self.check(TokenType::DAt) {
22150                    // @variable syntax (MySQL user variables)
22151                    self.skip(); // consume @
22152                    let mut name_str = "@".to_string();
22153                    let first = self.advance().text.clone();
22154                    name_str.push_str(&first);
22155                    Expression::Identifier(Identifier::new(name_str))
22156                } else {
22157                    let first = self.advance().text.clone();
22158                    let mut name_str = first;
22159                    // Handle dotted identifiers (e.g., schema.variable)
22160                    while self.match_token(TokenType::Dot) {
22161                        let next = self.advance().text.clone();
22162                        name_str.push('.');
22163                        name_str.push_str(&next);
22164                    }
22165                    // Handle Hive-style colon-separated names (e.g., hiveconf:some_var)
22166                    // But not := which is assignment
22167                    while self.check(TokenType::Colon) && !self.check_next(TokenType::Eq) {
22168                        self.skip(); // consume :
22169                        let next = self.advance().text.clone();
22170                        name_str.push(':');
22171                        name_str.push_str(&next);
22172                    }
22173                    Expression::Identifier(Identifier::new(name_str))
22174                }
22175            };
22176
22177            // Wrap name with VARIABLE marker if SET VARIABLE was used
22178            let name = if is_variable {
22179                // Store as "VARIABLE name" identifier
22180                let name_str = match &name {
22181                    Expression::Column(col) => col.name.name.clone(),
22182                    Expression::Identifier(id) => id.name.clone(),
22183                    _ => format!("{:?}", name),
22184                };
22185                Expression::Identifier(Identifier::new(format!("VARIABLE {}", name_str)))
22186            } else {
22187                name
22188            };
22189
22190            // Expect = or := or TO
22191            if self.match_token(TokenType::Eq) || self.match_token(TokenType::ColonEq) {
22192                // ok - standard assignment
22193            } else if self.match_token(TokenType::To) {
22194                // PostgreSQL uses SET var TO value
22195            } else if self.is_at_end()
22196                || self.check(TokenType::Semicolon)
22197                || self.check(TokenType::Comma)
22198            {
22199                // SET x ON/OFF without = (TSQL: SET XACT_ABORT ON)
22200                // The ON/OFF was already parsed as part of the name expression
22201                // Handle as a name-only set (value is empty)
22202                items.push(SetItem {
22203                    name,
22204                    value: Expression::Identifier(Identifier::new("".to_string())),
22205                    kind,
22206                    no_equals: false,
22207                });
22208                if !self.match_token(TokenType::Comma) {
22209                    break;
22210                }
22211                continue;
22212            } else {
22213                // Check if the next token looks like a value (ON/OFF without =)
22214                // TSQL: SET XACT_ABORT ON, SET NOCOUNT ON
22215                if self.check(TokenType::On) || self.check_keyword_text("OFF") {
22216                    let val = self.advance().text;
22217                    // Include ON/OFF in the name so generator doesn't add "="
22218                    let name_with_val = match &name {
22219                        Expression::Column(col) => format!("{} {}", col.name.name, val),
22220                        Expression::Identifier(id) => format!("{} {}", id.name, val),
22221                        _ => val.clone(),
22222                    };
22223                    items.push(SetItem {
22224                        name: Expression::Identifier(Identifier::new(name_with_val)),
22225                        value: Expression::Identifier(Identifier::new("".to_string())),
22226                        kind,
22227                        no_equals: false,
22228                    });
22229                    if !self.match_token(TokenType::Comma) {
22230                        break;
22231                    }
22232                    continue;
22233                }
22234                // TSQL/Generic: SET key value (without = or TO)
22235                // Parse the next token as the value
22236                if !self.is_at_end() && !self.check(TokenType::Semicolon) {
22237                    let value = self.parse_expression()?;
22238                    items.push(SetItem {
22239                        name,
22240                        value,
22241                        kind,
22242                        no_equals: true,
22243                    });
22244                    if !self.match_token(TokenType::Comma) {
22245                        break;
22246                    }
22247                    continue;
22248                }
22249                return Err(self.parse_error("Expected '=' or 'TO' in SET statement"));
22250            }
22251
22252            // Parse value - handle ON/OFF keywords as identifiers (MySQL: SET autocommit = ON)
22253            let value = if self.check(TokenType::On) || self.check_keyword_text("OFF") {
22254                Expression::Identifier(Identifier::new(self.advance().text.clone()))
22255            } else if self.match_token(TokenType::Default) {
22256                Expression::Identifier(Identifier::new("DEFAULT".to_string()))
22257            } else {
22258                self.parse_expression()?
22259            };
22260
22261            items.push(SetItem {
22262                name,
22263                value,
22264                kind,
22265                no_equals: false,
22266            });
22267
22268            if !self.match_token(TokenType::Comma) {
22269                break;
22270            }
22271        }
22272
22273        Ok(Expression::SetStatement(Box::new(SetStatement { items })))
22274    }
22275
22276    /// Parse Teradata SET QUERY_BAND statement
22277    fn parse_query_band(&mut self) -> Result<Expression> {
22278        self.expect(TokenType::Eq)?;
22279
22280        let value = if self.match_identifier("NONE") {
22281            Expression::Var(Box::new(Var {
22282                this: "NONE".to_string(),
22283            }))
22284        } else if self.check(TokenType::String) {
22285            Expression::Literal(Literal::String(self.expect_string()?))
22286        } else {
22287            self.parse_primary()?
22288        };
22289
22290        let update = if self.match_token(TokenType::Update) || self.match_identifier("UPDATE") {
22291            Some(Box::new(Expression::Boolean(BooleanLiteral {
22292                value: true,
22293            })))
22294        } else {
22295            None
22296        };
22297
22298        let _ = self.match_token(TokenType::For);
22299
22300        let scope = if self.match_token(TokenType::Session) || self.match_identifier("SESSION") {
22301            if self.match_identifier("VOLATILE") {
22302                Some("SESSION VOLATILE".to_string())
22303            } else {
22304                Some("SESSION".to_string())
22305            }
22306        } else if self.match_token(TokenType::Transaction) || self.match_identifier("TRANSACTION") {
22307            Some("TRANSACTION".to_string())
22308        } else if self.match_identifier("VOLATILE") {
22309            Some("VOLATILE".to_string())
22310        } else {
22311            None
22312        };
22313
22314        Ok(Expression::QueryBand(Box::new(QueryBand {
22315            this: Box::new(value),
22316            scope: scope.map(|s| Box::new(Expression::Var(Box::new(Var { this: s })))),
22317            update,
22318        })))
22319    }
22320
22321    /// Parse FETCH FIRST/NEXT clause
22322    fn parse_fetch(&mut self) -> Result<Fetch> {
22323        // FETCH [FIRST|NEXT] [count] [PERCENT] [ROW|ROWS] [ONLY|WITH TIES]
22324
22325        // FIRST or NEXT
22326        let direction = if self.match_token(TokenType::First) {
22327            "FIRST".to_string()
22328        } else if self.match_token(TokenType::Next) {
22329            "NEXT".to_string()
22330        } else {
22331            "FIRST".to_string() // Default
22332        };
22333
22334        // Optional count - but check if next token is ROW/ROWS/PERCENT/ONLY (no count)
22335        let count = if !self.check(TokenType::Row)
22336            && !self.check(TokenType::Rows)
22337            && !self.check(TokenType::Percent)
22338            && !self.check(TokenType::Only)
22339        {
22340            // Accept number, parenthesized expression, or TSQL @variable (Var token)
22341            if self.check(TokenType::Number)
22342                || self.check(TokenType::LParen)
22343                || self.check(TokenType::DAt)
22344                || self.check(TokenType::Var)
22345            {
22346                Some(self.parse_primary()?)
22347            } else {
22348                None
22349            }
22350        } else {
22351            None
22352        };
22353
22354        // PERCENT modifier
22355        let percent = self.match_token(TokenType::Percent);
22356
22357        // ROW or ROWS
22358        let rows = self.match_token(TokenType::Row) || self.match_token(TokenType::Rows);
22359
22360        // ONLY or WITH TIES
22361        self.match_token(TokenType::Only);
22362        let with_ties = self.match_keywords(&[TokenType::With, TokenType::Ties]);
22363
22364        Ok(Fetch {
22365            direction,
22366            count,
22367            percent,
22368            rows,
22369            with_ties,
22370        })
22371    }
22372
22373    /// Parse a qualified name (schema.table.column or just table)
22374    fn parse_qualified_name(&mut self) -> Result<Expression> {
22375        let first = self.expect_identifier_or_keyword()?;
22376        let mut parts = vec![first];
22377
22378        while self.match_token(TokenType::Dot) {
22379            let next = self.expect_identifier_or_keyword()?;
22380            parts.push(next);
22381        }
22382
22383        if parts.len() == 1 {
22384            Ok(Expression::Identifier(Identifier::new(parts.remove(0))))
22385        } else if parts.len() == 2 {
22386            Ok(Expression::boxed_column(Column {
22387                table: Some(Identifier::new(parts[0].clone())),
22388                name: Identifier::new(parts[1].clone()),
22389                join_mark: false,
22390                trailing_comments: Vec::new(),
22391                span: None,
22392                inferred_type: None,
22393            }))
22394        } else {
22395            // For 3+ parts, create a Column with concatenated table parts
22396            let column_name = parts.pop().unwrap();
22397            let table_name = parts.join(".");
22398            Ok(Expression::boxed_column(Column {
22399                table: Some(Identifier::new(table_name)),
22400                name: Identifier::new(column_name),
22401                join_mark: false,
22402                trailing_comments: Vec::new(),
22403                span: None,
22404                inferred_type: None,
22405            }))
22406        }
22407    }
22408
22409    // ==================== Phase 4: Additional DDL Parsing ====================
22410
22411    /// Parse CREATE SCHEMA statement
22412    fn parse_create_schema(&mut self, leading_comments: Vec<String>) -> Result<Expression> {
22413        self.expect(TokenType::Schema)?;
22414
22415        let if_not_exists =
22416            self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists]);
22417        let name = self.expect_identifier_with_quoted()?;
22418
22419        // Parse CLONE clause (Snowflake)
22420        let clone_from = if self.match_identifier("CLONE") {
22421            Some(self.expect_identifier_with_quoted()?)
22422        } else {
22423            None
22424        };
22425
22426        // Parse AT/BEFORE clause for time travel (Snowflake)
22427        // Note: BEFORE is a keyword token, AT is an identifier
22428        let at_clause = if self.match_identifier("AT") || self.match_token(TokenType::Before) {
22429            let keyword = self.previous().text.to_ascii_uppercase();
22430            self.expect(TokenType::LParen)?;
22431            // Parse the content: OFFSET => value or TIMESTAMP => value
22432            let mut result = format!("{} (", keyword);
22433            let mut prev_token_type: Option<TokenType> = None;
22434            let mut paren_depth = 1; // Track nested parens
22435            while !self.is_at_end() && paren_depth > 0 {
22436                let token = self.advance();
22437                if token.token_type == TokenType::LParen {
22438                    paren_depth += 1;
22439                } else if token.token_type == TokenType::RParen {
22440                    paren_depth -= 1;
22441                    if paren_depth == 0 {
22442                        break; // Don't include the closing paren in result yet
22443                    }
22444                }
22445                // Smart spacing: no space after ( or => or - and no space before (
22446                let needs_space = !result.ends_with('(')
22447                    && prev_token_type != Some(TokenType::Arrow)
22448                    && prev_token_type != Some(TokenType::Dash)
22449                    && prev_token_type != Some(TokenType::LParen)
22450                    && token.token_type != TokenType::LParen; // no space before (
22451                if needs_space
22452                    && token.token_type != TokenType::RParen
22453                    && token.token_type != TokenType::Comma
22454                {
22455                    result.push(' ');
22456                }
22457                // Properly quote string literals
22458                if token.token_type == TokenType::String {
22459                    result.push('\'');
22460                    result.push_str(&token.text.replace('\'', "''"));
22461                    result.push('\'');
22462                } else {
22463                    result.push_str(&token.text);
22464                }
22465                if token.token_type == TokenType::Arrow || token.token_type == TokenType::Comma {
22466                    result.push(' ');
22467                }
22468                prev_token_type = Some(token.token_type);
22469            }
22470            result.push(')');
22471            Some(Expression::Raw(Raw { sql: result }))
22472        } else {
22473            None
22474        };
22475
22476        let authorization = if self.match_token(TokenType::Authorization) {
22477            Some(Identifier::new(self.expect_identifier()?))
22478        } else {
22479            None
22480        };
22481
22482        // Parse schema properties like DEFAULT COLLATE or WITH (properties)
22483        let mut properties = Vec::new();
22484
22485        // Parse WITH (prop1=val1, prop2=val2, ...) (Trino/Presto)
22486        if self.match_token(TokenType::With) {
22487            self.expect(TokenType::LParen)?;
22488            loop {
22489                // Parse property name (identifier or string)
22490                let prop_name = if self.check(TokenType::String) {
22491                    Expression::Literal(Literal::String(self.expect_string()?))
22492                } else {
22493                    Expression::Identifier(Identifier::new(self.expect_identifier_or_keyword()?))
22494                };
22495                self.expect(TokenType::Eq)?;
22496                // Parse property value
22497                let prop_value = self.parse_expression()?;
22498                // Create Property expression: key=value
22499                properties.push(Expression::Property(Box::new(Property {
22500                    this: Box::new(prop_name),
22501                    value: Some(Box::new(prop_value)),
22502                })));
22503                if !self.match_token(TokenType::Comma) {
22504                    break;
22505                }
22506            }
22507            self.expect(TokenType::RParen)?;
22508        }
22509
22510        // Parse DEFAULT COLLATE 'value' (BigQuery)
22511        if self.match_token(TokenType::Default) && self.match_token(TokenType::Collate) {
22512            // Parse the collation value (could be string literal or identifier)
22513            let collation = self.parse_primary()?;
22514            properties.push(Expression::CollateProperty(Box::new(CollateProperty {
22515                this: Box::new(collation),
22516                default: Some(Box::new(Expression::Boolean(BooleanLiteral {
22517                    value: true,
22518                }))),
22519            })));
22520        }
22521
22522        Ok(Expression::CreateSchema(Box::new(CreateSchema {
22523            name,
22524            if_not_exists,
22525            authorization,
22526            clone_from,
22527            at_clause,
22528            properties,
22529            leading_comments,
22530        })))
22531    }
22532
22533    /// Parse DROP SCHEMA statement
22534    fn parse_drop_schema(&mut self) -> Result<Expression> {
22535        self.expect(TokenType::Schema)?;
22536
22537        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
22538        let name = Identifier::new(self.expect_identifier()?);
22539
22540        let cascade = self.match_token(TokenType::Cascade);
22541        if !cascade {
22542            self.match_token(TokenType::Restrict);
22543        }
22544
22545        Ok(Expression::DropSchema(Box::new(DropSchema {
22546            name,
22547            if_exists,
22548            cascade,
22549        })))
22550    }
22551
22552    /// Parse CREATE DATABASE statement
22553    fn parse_create_database(&mut self) -> Result<Expression> {
22554        self.expect(TokenType::Database)?;
22555
22556        let if_not_exists =
22557            self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists]);
22558        let name = Identifier::new(self.expect_identifier()?);
22559
22560        // Check for Snowflake CLONE clause
22561        let clone_from = if self.match_identifier("CLONE") {
22562            Some(Identifier::new(self.expect_identifier()?))
22563        } else {
22564            None
22565        };
22566
22567        // Parse AT/BEFORE clause for time travel (Snowflake)
22568        // Note: BEFORE is a keyword token, AT is an identifier
22569        let at_clause = if self.match_identifier("AT") || self.match_token(TokenType::Before) {
22570            let keyword = self.previous().text.to_ascii_uppercase();
22571            self.expect(TokenType::LParen)?;
22572            // Parse the content: OFFSET => value or TIMESTAMP => value
22573            let mut result = format!("{} (", keyword);
22574            let mut prev_token_type: Option<TokenType> = None;
22575            let mut paren_depth = 1; // Track nested parens
22576            while !self.is_at_end() && paren_depth > 0 {
22577                let token = self.advance();
22578                if token.token_type == TokenType::LParen {
22579                    paren_depth += 1;
22580                } else if token.token_type == TokenType::RParen {
22581                    paren_depth -= 1;
22582                    if paren_depth == 0 {
22583                        break; // Don't include the closing paren in result yet
22584                    }
22585                }
22586                // Smart spacing: no space after ( or => or - and no space before (
22587                let needs_space = !result.ends_with('(')
22588                    && prev_token_type != Some(TokenType::Arrow)
22589                    && prev_token_type != Some(TokenType::Dash)
22590                    && prev_token_type != Some(TokenType::LParen)
22591                    && token.token_type != TokenType::LParen; // no space before (
22592                if needs_space
22593                    && token.token_type != TokenType::RParen
22594                    && token.token_type != TokenType::Comma
22595                {
22596                    result.push(' ');
22597                }
22598                // Properly quote string literals
22599                if token.token_type == TokenType::String {
22600                    result.push('\'');
22601                    result.push_str(&token.text.replace('\'', "''"));
22602                    result.push('\'');
22603                } else {
22604                    result.push_str(&token.text);
22605                }
22606                if token.token_type == TokenType::Arrow || token.token_type == TokenType::Comma {
22607                    result.push(' ');
22608                }
22609                prev_token_type = Some(token.token_type);
22610            }
22611            result.push(')');
22612            Some(Expression::Raw(Raw { sql: result }))
22613        } else {
22614            None
22615        };
22616
22617        // ClickHouse: ON CLUSTER clause
22618        let _on_cluster = self.parse_on_cluster_clause()?;
22619
22620        let mut options = Vec::new();
22621
22622        // Parse database options
22623        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
22624            if self.match_identifier("OWNER") || self.match_token(TokenType::Eq) {
22625                self.match_token(TokenType::Eq);
22626                options.push(DatabaseOption::Owner(Identifier::new(
22627                    self.expect_identifier()?,
22628                )));
22629            } else if self.match_identifier("TEMPLATE") {
22630                self.match_token(TokenType::Eq);
22631                options.push(DatabaseOption::Template(Identifier::new(
22632                    self.expect_identifier()?,
22633                )));
22634            } else if self.match_identifier("ENCODING") {
22635                self.match_token(TokenType::Eq);
22636                let encoding = if self.check(TokenType::String) {
22637                    let tok = self.advance();
22638                    tok.text.trim_matches('\'').to_string()
22639                } else {
22640                    self.expect_identifier()?
22641                };
22642                options.push(DatabaseOption::Encoding(encoding));
22643            } else if self.match_identifier("CHARACTER") {
22644                self.match_token(TokenType::Set);
22645                self.match_token(TokenType::Eq);
22646                let charset = if self.check(TokenType::String) {
22647                    let tok = self.advance();
22648                    tok.text.trim_matches('\'').to_string()
22649                } else {
22650                    self.expect_identifier()?
22651                };
22652                options.push(DatabaseOption::CharacterSet(charset));
22653            } else if self.match_identifier("COLLATE") {
22654                self.match_token(TokenType::Eq);
22655                let collate = if self.check(TokenType::String) {
22656                    let tok = self.advance();
22657                    tok.text.trim_matches('\'').to_string()
22658                } else {
22659                    self.expect_identifier()?
22660                };
22661                options.push(DatabaseOption::Collate(collate));
22662            } else if self.match_identifier("LOCATION") {
22663                self.match_token(TokenType::Eq);
22664                let loc = if self.check(TokenType::String) {
22665                    let tok = self.advance();
22666                    tok.text.trim_matches('\'').to_string()
22667                } else {
22668                    self.expect_identifier()?
22669                };
22670                options.push(DatabaseOption::Location(loc));
22671            } else {
22672                break;
22673            }
22674        }
22675
22676        Ok(Expression::CreateDatabase(Box::new(CreateDatabase {
22677            name,
22678            if_not_exists,
22679            options,
22680            clone_from,
22681            at_clause,
22682        })))
22683    }
22684
22685    /// Parse DROP DATABASE statement
22686    fn parse_drop_database(&mut self) -> Result<Expression> {
22687        self.expect(TokenType::Database)?;
22688
22689        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
22690
22691        // ClickHouse: IF EMPTY
22692        if !if_exists
22693            && matches!(
22694                self.config.dialect,
22695                Some(crate::dialects::DialectType::ClickHouse)
22696            )
22697        {
22698            if self.check(TokenType::If)
22699                && self.current + 1 < self.tokens.len()
22700                && self.tokens[self.current + 1]
22701                    .text
22702                    .eq_ignore_ascii_case("EMPTY")
22703            {
22704                self.skip(); // consume IF
22705                self.skip(); // consume EMPTY
22706            }
22707        }
22708        let name = Identifier::new(self.expect_identifier()?);
22709
22710        // ClickHouse: ON CLUSTER clause
22711        if matches!(
22712            self.config.dialect,
22713            Some(crate::dialects::DialectType::ClickHouse)
22714        ) {
22715            let _ = self.parse_on_cluster_clause()?;
22716            self.match_identifier("SYNC");
22717        }
22718
22719        Ok(Expression::DropDatabase(Box::new(DropDatabase {
22720            name,
22721            if_exists,
22722        })))
22723    }
22724
22725    /// Parse CREATE FUNCTION statement
22726    fn parse_create_function(
22727        &mut self,
22728        or_replace: bool,
22729        temporary: bool,
22730        is_table_function: bool,
22731    ) -> Result<Expression> {
22732        self.expect(TokenType::Function)?;
22733
22734        let if_not_exists =
22735            self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists]);
22736        let name = self.parse_table_ref()?;
22737
22738        // Parse parameters (optional - some dialects allow CREATE FUNCTION f AS 'body')
22739        let (parameters, has_parens) = if self.match_token(TokenType::LParen) {
22740            let params = self.parse_function_parameters()?;
22741            self.expect(TokenType::RParen)?;
22742            (params, true)
22743        } else {
22744            (Vec::new(), false)
22745        };
22746
22747        // Track if LANGUAGE appears before RETURNS
22748        let mut language_first = false;
22749        let mut return_type = None;
22750        let mut language = None;
22751        let mut sql_data_access = None;
22752
22753        // Check for LANGUAGE before RETURNS
22754        if self.match_token(TokenType::Language) {
22755            language = Some(self.expect_identifier_or_keyword()?);
22756            language_first = true;
22757        }
22758
22759        // Parse RETURNS clause (may come before or after LANGUAGE)
22760        let mut returns_table_body: Option<String> = None;
22761        if self.match_token(TokenType::Returns) {
22762            if self.check(TokenType::Var) && self.peek().text.starts_with('@') {
22763                // TSQL: RETURNS @var TABLE (col_defs)
22764                let var_name = self.advance().text.clone();
22765                if self.check(TokenType::Table) {
22766                    self.skip(); // consume TABLE
22767                    return_type = Some(DataType::Custom {
22768                        name: "TABLE".to_string(),
22769                    });
22770                    // Parse column definitions
22771                    if self.match_token(TokenType::LParen) {
22772                        let start = self.current;
22773                        let mut depth = 1;
22774                        while depth > 0 && !self.is_at_end() {
22775                            if self.check(TokenType::LParen) {
22776                                depth += 1;
22777                            }
22778                            if self.check(TokenType::RParen) {
22779                                depth -= 1;
22780                                if depth == 0 {
22781                                    break;
22782                                }
22783                            }
22784                            self.skip();
22785                        }
22786                        // Reconstruct the column definitions with proper spacing
22787                        let mut col_defs_str = String::new();
22788                        for (i, tok) in self.tokens[start..self.current].iter().enumerate() {
22789                            // Don't add space before comma, LParen, RParen
22790                            // Don't add space after LParen
22791                            let prev_tok = if i > 0 {
22792                                Some(&self.tokens[start + i - 1])
22793                            } else {
22794                                None
22795                            };
22796                            let needs_space = i > 0
22797                                && tok.token_type != TokenType::Comma
22798                                && tok.token_type != TokenType::RParen
22799                                && tok.token_type != TokenType::LParen
22800                                && prev_tok
22801                                    .map(|p| p.token_type != TokenType::LParen)
22802                                    .unwrap_or(true);
22803                            if needs_space {
22804                                col_defs_str.push(' ');
22805                            }
22806                            col_defs_str.push_str(&tok.text);
22807                        }
22808                        returns_table_body = Some(format!("{} TABLE ({})", var_name, col_defs_str));
22809                        self.expect(TokenType::RParen)?;
22810                    } else {
22811                        returns_table_body = Some(format!("{} TABLE", var_name));
22812                    }
22813                } else {
22814                    // Parse data type after var name
22815                    return_type = Some(self.parse_data_type()?);
22816                }
22817            } else if self.check(TokenType::Table) {
22818                // Could be:
22819                // - TSQL: RETURNS TABLE AS RETURN ...
22820                // - BigQuery: RETURNS TABLE <col1 TYPE, col2 TYPE>
22821                // - Snowflake: RETURNS TABLE(col1 TYPE, col2 TYPE)
22822                self.skip(); // consume TABLE
22823                if self.check(TokenType::Lt) {
22824                    // BigQuery: RETURNS TABLE <col1 TYPE, col2 TYPE>
22825                    self.skip(); // consume <
22826                    let mut cols = Vec::new();
22827                    loop {
22828                        let col_name = self.expect_identifier()?;
22829                        let col_type = self.parse_data_type()?;
22830                        cols.push(format!(
22831                            "{} {}",
22832                            col_name,
22833                            self.data_type_to_string(&col_type)
22834                        ));
22835                        if !self.match_token(TokenType::Comma) {
22836                            break;
22837                        }
22838                    }
22839                    if !self.match_token(TokenType::Gt) {
22840                        return Err(self.parse_error("Expected > after TABLE column definitions"));
22841                    }
22842                    returns_table_body = Some(format!("TABLE <{}>", cols.join(", ")));
22843                } else if self.check(TokenType::LParen) {
22844                    // Snowflake: RETURNS TABLE(col1 TYPE, col2 TYPE)
22845                    self.skip(); // consume (
22846                    let mut cols = Vec::new();
22847                    loop {
22848                        let col_name = self.expect_identifier()?;
22849                        let col_type = self.parse_data_type()?;
22850                        cols.push(format!(
22851                            "{} {}",
22852                            col_name,
22853                            self.data_type_to_string(&col_type)
22854                        ));
22855                        if !self.match_token(TokenType::Comma) {
22856                            break;
22857                        }
22858                    }
22859                    self.expect(TokenType::RParen)?;
22860                    returns_table_body = Some(format!("TABLE ({})", cols.join(", ")));
22861                } else {
22862                    // TSQL: RETURNS TABLE AS RETURN ...
22863                    return_type = Some(DataType::Custom {
22864                        name: "TABLE".to_string(),
22865                    });
22866                }
22867            } else {
22868                // Use parse_function_return_type to preserve original type names like 'integer'
22869                return_type = Some(self.parse_function_return_type()?);
22870            }
22871        }
22872
22873        let mut deterministic = None;
22874        let mut returns_null_on_null_input = None;
22875        let mut strict = false;
22876        let mut security = None;
22877        let mut body = None;
22878        let mut set_options: Vec<FunctionSetOption> = Vec::new();
22879        let mut property_order: Vec<FunctionPropertyKind> = Vec::new();
22880        let mut options: Vec<Expression> = Vec::new();
22881        let mut environment: Vec<Expression> = Vec::new();
22882
22883        // Parse function options
22884        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
22885            if self.check(TokenType::Returns)
22886                && self.current + 1 < self.tokens.len()
22887                && self.tokens[self.current + 1].token_type == TokenType::Null
22888            {
22889                // RETURNS NULL ON NULL INPUT
22890                self.skip(); // consume RETURNS
22891                self.skip(); // consume NULL
22892                self.match_token(TokenType::On);
22893                self.match_token(TokenType::Null);
22894                self.match_token(TokenType::Input);
22895                returns_null_on_null_input = Some(true);
22896                if !property_order.contains(&FunctionPropertyKind::NullInput) {
22897                    property_order.push(FunctionPropertyKind::NullInput);
22898                }
22899            } else if self.match_token(TokenType::Returns) {
22900                // RETURNS can come after LANGUAGE
22901                return_type = Some(self.parse_data_type()?);
22902            } else if self.match_token(TokenType::Language) {
22903                // Language can be SQL, PLPGSQL, PYTHON, etc.
22904                language = Some(self.expect_identifier_or_keyword()?);
22905                if !property_order.contains(&FunctionPropertyKind::Language) {
22906                    property_order.push(FunctionPropertyKind::Language);
22907                }
22908            } else if self.match_token(TokenType::Not) && self.match_identifier("DETERMINISTIC") {
22909                deterministic = Some(false);
22910                if !property_order.contains(&FunctionPropertyKind::Determinism) {
22911                    property_order.push(FunctionPropertyKind::Determinism);
22912                }
22913            } else if self.match_identifier("DETERMINISTIC") {
22914                deterministic = Some(true);
22915                if !property_order.contains(&FunctionPropertyKind::Determinism) {
22916                    property_order.push(FunctionPropertyKind::Determinism);
22917                }
22918            } else if self.match_identifier("IMMUTABLE") {
22919                deterministic = Some(true);
22920                if !property_order.contains(&FunctionPropertyKind::Determinism) {
22921                    property_order.push(FunctionPropertyKind::Determinism);
22922                }
22923            } else if self.match_identifier("STABLE") || self.match_identifier("VOLATILE") {
22924                deterministic = Some(false);
22925                if !property_order.contains(&FunctionPropertyKind::Determinism) {
22926                    property_order.push(FunctionPropertyKind::Determinism);
22927                }
22928            } else if self.match_identifier("STRICT") {
22929                returns_null_on_null_input = Some(true);
22930                strict = true;
22931                if !property_order.contains(&FunctionPropertyKind::NullInput) {
22932                    property_order.push(FunctionPropertyKind::NullInput);
22933                }
22934            } else if self.match_identifier("CALLED") {
22935                self.match_token(TokenType::On);
22936                self.match_token(TokenType::Null);
22937                self.match_token(TokenType::Input);
22938                returns_null_on_null_input = Some(false);
22939                if !property_order.contains(&FunctionPropertyKind::NullInput) {
22940                    property_order.push(FunctionPropertyKind::NullInput);
22941                }
22942            } else if self.match_identifier("SECURITY") {
22943                if self.match_identifier("DEFINER") {
22944                    security = Some(FunctionSecurity::Definer);
22945                } else if self.match_identifier("INVOKER") {
22946                    security = Some(FunctionSecurity::Invoker);
22947                }
22948                if !property_order.contains(&FunctionPropertyKind::Security) {
22949                    property_order.push(FunctionPropertyKind::Security);
22950                }
22951            } else if self.match_identifier("CONTAINS") {
22952                // CONTAINS SQL
22953                self.match_identifier("SQL");
22954                sql_data_access = Some(SqlDataAccess::ContainsSql);
22955                if !property_order.contains(&FunctionPropertyKind::SqlDataAccess) {
22956                    property_order.push(FunctionPropertyKind::SqlDataAccess);
22957                }
22958            } else if self.match_identifier("READS") {
22959                // READS SQL DATA
22960                self.match_identifier("SQL");
22961                self.match_identifier("DATA");
22962                sql_data_access = Some(SqlDataAccess::ReadsSqlData);
22963                if !property_order.contains(&FunctionPropertyKind::SqlDataAccess) {
22964                    property_order.push(FunctionPropertyKind::SqlDataAccess);
22965                }
22966            } else if self.match_identifier("MODIFIES") {
22967                // MODIFIES SQL DATA
22968                self.match_identifier("SQL");
22969                self.match_identifier("DATA");
22970                sql_data_access = Some(SqlDataAccess::ModifiesSqlData);
22971                if !property_order.contains(&FunctionPropertyKind::SqlDataAccess) {
22972                    property_order.push(FunctionPropertyKind::SqlDataAccess);
22973                }
22974            } else if self.match_token(TokenType::No) && self.match_identifier("SQL") {
22975                // NO SQL
22976                sql_data_access = Some(SqlDataAccess::NoSql);
22977                if !property_order.contains(&FunctionPropertyKind::SqlDataAccess) {
22978                    property_order.push(FunctionPropertyKind::SqlDataAccess);
22979                }
22980            } else if self.match_token(TokenType::Set) {
22981                // PostgreSQL: SET key = value / SET key TO value / SET key FROM CURRENT
22982                let opt_name = self.expect_identifier_or_keyword()?;
22983                let value = if self.match_token(TokenType::From) {
22984                    // SET key FROM CURRENT
22985                    if !self.match_token(TokenType::Current) {
22986                        return Err(self.parse_error("Expected CURRENT after FROM in SET option"));
22987                    }
22988                    FunctionSetValue::FromCurrent
22989                } else {
22990                    // SET key = value or SET key TO value
22991                    let use_to = self.match_token(TokenType::To);
22992                    if !use_to && !self.match_token(TokenType::Eq) {
22993                        return Err(self.parse_error("Expected = or TO after SET key"));
22994                    }
22995                    // Value can be a string literal or identifier
22996                    let val = if self.check(TokenType::String) {
22997                        let tok = self.advance();
22998                        format!("'{}'", tok.text)
22999                    } else {
23000                        self.expect_identifier_or_keyword()?
23001                    };
23002                    FunctionSetValue::Value { value: val, use_to }
23003                };
23004                set_options.push(FunctionSetOption {
23005                    name: opt_name,
23006                    value,
23007                });
23008                if !property_order.contains(&FunctionPropertyKind::Set) {
23009                    property_order.push(FunctionPropertyKind::Set);
23010                }
23011            } else if self.match_token(TokenType::As) {
23012                // Parse function body: AS RETURN x, AS $$ ... $$, AS BEGIN ... END, AS 'body'
23013                if !property_order.contains(&FunctionPropertyKind::As) {
23014                    property_order.push(FunctionPropertyKind::As);
23015                }
23016                if self.match_identifier("RETURN") {
23017                    // AS RETURN expression (or SELECT statement for TSQL TVFs)
23018                    let expr = if self.check(TokenType::Select) || self.check(TokenType::With) {
23019                        // TSQL: AS RETURN SELECT ... for table-valued functions
23020                        self.parse_statement()?
23021                    } else {
23022                        self.parse_expression()?
23023                    };
23024                    body = Some(FunctionBody::Return(expr));
23025                } else if self.check(TokenType::Select) || self.check(TokenType::With) {
23026                    // TSQL: AS SELECT ... for table-valued functions (without RETURN keyword)
23027                    let stmt = self.parse_statement()?;
23028                    body = Some(FunctionBody::Expression(stmt));
23029                } else if self.check(TokenType::DollarString) {
23030                    let tok = self.advance();
23031                    // Parse the dollar string token to extract tag and content
23032                    let (tag, content) = crate::tokens::parse_dollar_string_token(&tok.text);
23033                    body = Some(FunctionBody::DollarQuoted { content, tag });
23034                } else if self.check(TokenType::String) {
23035                    let tok = self.advance();
23036                    body = Some(FunctionBody::StringLiteral(tok.text.clone()));
23037                } else if self.match_token(TokenType::Begin) {
23038                    // Parse BEGIN...END block
23039                    let mut block_content = String::new();
23040                    let mut depth = 1;
23041                    while depth > 0 && !self.is_at_end() {
23042                        let tok = self.advance();
23043                        if tok.token_type == TokenType::Begin {
23044                            depth += 1;
23045                        } else if tok.token_type == TokenType::End {
23046                            depth -= 1;
23047                            if depth == 0 {
23048                                break;
23049                            }
23050                        }
23051                        block_content.push_str(&tok.text);
23052                        block_content.push(' ');
23053                    }
23054                    body = Some(FunctionBody::Block(block_content.trim().to_string()));
23055                } else {
23056                    // Expression-based body
23057                    let expr = self.parse_expression()?;
23058                    body = Some(FunctionBody::Expression(expr));
23059                }
23060            } else if self.match_identifier("RETURN") {
23061                // RETURN expression (or SELECT statement for TSQL TVFs)
23062                let expr = if self.check(TokenType::Select) || self.check(TokenType::With) {
23063                    self.parse_statement()?
23064                } else {
23065                    self.parse_expression()?
23066                };
23067                body = Some(FunctionBody::Return(expr));
23068            } else if self.match_identifier("EXTERNAL") {
23069                self.match_identifier("NAME");
23070                let ext_name = if self.check(TokenType::String) {
23071                    let tok = self.advance();
23072                    tok.text.trim_matches('\'').to_string()
23073                } else {
23074                    self.expect_identifier()?
23075                };
23076                body = Some(FunctionBody::External(ext_name));
23077            } else if self.match_identifier("OPTIONS") {
23078                // BigQuery: OPTIONS (key=value, ...) - track in property_order
23079                let parsed_options = self.parse_options_list()?;
23080                options.extend(parsed_options);
23081                if !property_order.contains(&FunctionPropertyKind::Options) {
23082                    property_order.push(FunctionPropertyKind::Options);
23083                }
23084            } else if self.match_identifier("ENVIRONMENT") {
23085                // Databricks: ENVIRONMENT (dependencies = '...', environment_version = '...')
23086                let parsed_env = self.parse_environment_list()?;
23087                environment.extend(parsed_env);
23088                if !property_order.contains(&FunctionPropertyKind::Environment) {
23089                    property_order.push(FunctionPropertyKind::Environment);
23090                }
23091            } else {
23092                break;
23093            }
23094        }
23095
23096        // BigQuery: OPTIONS (key=value, ...) can also appear after AS body (legacy position)
23097        if options.is_empty() && self.match_identifier("OPTIONS") {
23098            let parsed_options = self.parse_options_list()?;
23099            options.extend(parsed_options);
23100            if !property_order.contains(&FunctionPropertyKind::Options) {
23101                property_order.push(FunctionPropertyKind::Options);
23102            }
23103        }
23104
23105        Ok(Expression::CreateFunction(Box::new(CreateFunction {
23106            name,
23107            parameters,
23108            return_type,
23109            body,
23110            or_replace,
23111            if_not_exists,
23112            temporary,
23113            language,
23114            deterministic,
23115            returns_null_on_null_input,
23116            security,
23117            has_parens,
23118            sql_data_access,
23119            returns_table_body,
23120            language_first,
23121            set_options,
23122            strict,
23123            options,
23124            is_table_function,
23125            property_order,
23126            environment,
23127        })))
23128    }
23129
23130    /// Parse function parameters
23131    fn parse_function_parameters(&mut self) -> Result<Vec<FunctionParameter>> {
23132        let mut params = Vec::new();
23133
23134        if self.check(TokenType::RParen) {
23135            return Ok(params);
23136        }
23137
23138        loop {
23139            let mut mode = None;
23140            let mut mode_text: Option<String> = None;
23141
23142            // Check for parameter mode (IN, OUT, INOUT, VARIADIC)
23143            // Note: OUT, INOUT, VARIADIC are tokenized as Var, not as dedicated keywords
23144            if self.match_token(TokenType::In) {
23145                // IN or IN OUT
23146                if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("OUT") {
23147                    let out_text = self.advance().text.clone(); // consume OUT
23148                    mode_text = Some(format!("IN {}", out_text));
23149                    mode = Some(ParameterMode::InOut);
23150                } else {
23151                    mode_text = Some("IN".to_string());
23152                    mode = Some(ParameterMode::In);
23153                }
23154            } else if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("OUT") {
23155                let text = self.advance().text.clone();
23156                mode_text = Some(text);
23157                mode = Some(ParameterMode::Out);
23158            } else if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("INOUT") {
23159                let text = self.advance().text.clone();
23160                mode_text = Some(text);
23161                mode = Some(ParameterMode::InOut);
23162            } else if self.check(TokenType::Var)
23163                && self.peek().text.eq_ignore_ascii_case("VARIADIC")
23164            {
23165                let text = self.advance().text.clone();
23166                mode_text = Some(text);
23167                mode = Some(ParameterMode::Variadic);
23168            }
23169
23170            // Try to parse name and type
23171            // After a mode keyword (VARIADIC, OUT, etc.), the next thing could be:
23172            //   - a type directly (e.g., VARIADIC INT[], OUT INT)
23173            //   - a name then a type (e.g., VARIADIC a INT[], OUT result INT)
23174            //
23175            // Strategy: use backtracking. Save position, try parsing as data type.
23176            // If the result is followed by , or ) or DEFAULT, it was a type-only param.
23177            // Otherwise, restore position and parse as name + type.
23178            let (name, data_type) = if mode.is_some() {
23179                let saved = self.current;
23180                // Try parsing as a data type directly
23181                let type_result = self.parse_data_type();
23182                if let Ok(dt) = type_result {
23183                    if self.check(TokenType::Comma)
23184                        || self.check(TokenType::RParen)
23185                        || self.check(TokenType::Default)
23186                        || self.check(TokenType::Eq)
23187                    {
23188                        // Successfully parsed as a type-only parameter
23189                        (None, dt)
23190                    } else {
23191                        // Not followed by comma/rparen — restore and parse as name + type
23192                        self.current = saved;
23193                        let first_ident =
23194                            if self.check(TokenType::Input) || self.check(TokenType::Output) {
23195                                let token = self.advance();
23196                                Identifier {
23197                                    name: token.text,
23198                                    quoted: false,
23199                                    trailing_comments: Vec::new(),
23200                                    span: None,
23201                                }
23202                            } else {
23203                                self.expect_identifier_with_quoted()?
23204                            };
23205                        self.match_token(TokenType::As);
23206                        let dt = self.parse_data_type()?;
23207                        (Some(first_ident), dt)
23208                    }
23209                } else {
23210                    // Type parse failed — restore and try as name + type
23211                    self.current = saved;
23212                    let first_ident =
23213                        if self.check(TokenType::Input) || self.check(TokenType::Output) {
23214                            let token = self.advance();
23215                            Identifier {
23216                                name: token.text,
23217                                quoted: false,
23218                                trailing_comments: Vec::new(),
23219                                span: None,
23220                            }
23221                        } else {
23222                            self.expect_identifier_with_quoted()?
23223                        };
23224                    if self.check(TokenType::Comma)
23225                        || self.check(TokenType::RParen)
23226                        || self.check(TokenType::Default)
23227                    {
23228                        (None, self.identifier_to_datatype(&first_ident.name)?)
23229                    } else {
23230                        self.match_token(TokenType::As);
23231                        let dt = self.parse_data_type()?;
23232                        (Some(first_ident), dt)
23233                    }
23234                }
23235            } else {
23236                // No mode keyword — original logic
23237                // Handle keywords like INPUT that may be used as parameter names
23238                let first_ident = if self.check(TokenType::Input) || self.check(TokenType::Output) {
23239                    let token = self.advance();
23240                    Identifier {
23241                        name: token.text,
23242                        quoted: false,
23243                        trailing_comments: Vec::new(),
23244                        span: None,
23245                    }
23246                } else {
23247                    self.expect_identifier_with_quoted()?
23248                };
23249
23250                // Check if next token is a type or if this was the type
23251                if self.check(TokenType::Comma)
23252                    || self.check(TokenType::RParen)
23253                    || self.check(TokenType::Default)
23254                {
23255                    // This was the type, no name
23256                    (None, self.identifier_to_datatype(&first_ident.name)?)
23257                } else {
23258                    // This was the name, next is type
23259                    // TSQL allows: @param AS type (optional AS keyword)
23260                    self.match_token(TokenType::As);
23261                    let dt = self.parse_data_type()?;
23262                    (Some(first_ident), dt)
23263                }
23264            };
23265
23266            let default = if self.match_token(TokenType::Default) || self.match_token(TokenType::Eq)
23267            {
23268                Some(self.parse_expression()?)
23269            } else {
23270                None
23271            };
23272
23273            params.push(FunctionParameter {
23274                name,
23275                data_type,
23276                mode,
23277                default,
23278                mode_text: mode_text.clone(),
23279            });
23280
23281            if !self.match_token(TokenType::Comma) {
23282                break;
23283            }
23284        }
23285
23286        Ok(params)
23287    }
23288
23289    /// Parse TSQL-style unparenthesized procedure parameters
23290    /// Format: @param1 TYPE, @param2 TYPE, ... AS
23291    fn parse_tsql_procedure_params(&mut self) -> Result<Vec<FunctionParameter>> {
23292        let mut params = Vec::new();
23293        loop {
23294            if !self.check(TokenType::Var) {
23295                break;
23296            }
23297            let name = self.advance().text.clone();
23298            // Skip optional AS keyword between name and type
23299            self.match_token(TokenType::As);
23300            let data_type = self.parse_data_type()?;
23301            let default = if self.match_token(TokenType::Default) || self.match_token(TokenType::Eq)
23302            {
23303                Some(self.parse_expression()?)
23304            } else {
23305                None
23306            };
23307            params.push(FunctionParameter {
23308                name: Some(Identifier::new(name)),
23309                data_type,
23310                mode: None,
23311                default,
23312                mode_text: None,
23313            });
23314            if !self.match_token(TokenType::Comma) {
23315                break;
23316            }
23317        }
23318        Ok(params)
23319    }
23320
23321    /// Convert identifier to DataType for function parameters.
23322    /// Preserves the original identifier name to maintain exact type name as written.
23323    /// This matches Python sqlglot's behavior where function parameter types like 'integer'
23324    /// are stored as Identifiers rather than normalized DataTypes.
23325    fn identifier_to_datatype(&self, ident: &str) -> Result<DataType> {
23326        // Always use DataType::Custom to preserve the exact type name as written.
23327        // This is important for identity tests where e.g. 'integer' should not be normalized to 'INT'.
23328        Ok(DataType::Custom {
23329            name: ident.to_string(),
23330        })
23331    }
23332
23333    /// Parse a data type for function RETURNS clause, preserving original type names.
23334    /// For simple type names like 'integer', preserves the original name rather than
23335    /// normalizing to INT. This matches Python sqlglot's behavior.
23336    fn parse_function_return_type(&mut self) -> Result<DataType> {
23337        // Check if it's a simple identifier that could be a type name
23338        if (self.check(TokenType::Identifier) || self.check(TokenType::Var))
23339            && !self.check_next(TokenType::LParen)  // Not a parameterized type like VARCHAR(10)
23340            && !self.check_next(TokenType::LBracket)
23341        // Not an array type
23342        {
23343            let type_name = self.advance().text.clone();
23344            // Check if the next token indicates we should use parse_data_type instead
23345            // For complex types, fall through to parse_data_type
23346            return Ok(DataType::Custom { name: type_name });
23347        }
23348
23349        // For complex types, use standard parsing
23350        self.parse_data_type()
23351    }
23352
23353    /// Parse DROP FUNCTION statement
23354    fn parse_drop_function(&mut self) -> Result<Expression> {
23355        self.expect(TokenType::Function)?;
23356
23357        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
23358        let name = self.parse_table_ref()?;
23359
23360        // Optional parameter types for overloaded functions
23361        let parameters = if self.match_token(TokenType::LParen) {
23362            let mut types = Vec::new();
23363            if !self.check(TokenType::RParen) {
23364                loop {
23365                    types.push(self.parse_data_type()?);
23366                    if !self.match_token(TokenType::Comma) {
23367                        break;
23368                    }
23369                }
23370            }
23371            self.expect(TokenType::RParen)?;
23372            Some(types)
23373        } else {
23374            None
23375        };
23376
23377        let cascade = self.match_token(TokenType::Cascade);
23378        if !cascade {
23379            self.match_token(TokenType::Restrict);
23380        }
23381
23382        Ok(Expression::DropFunction(Box::new(DropFunction {
23383            name,
23384            parameters,
23385            if_exists,
23386            cascade,
23387        })))
23388    }
23389
23390    /// Parse CREATE PROCEDURE statement
23391    fn parse_create_procedure(&mut self, or_replace: bool) -> Result<Expression> {
23392        // Check if PROC shorthand was used before consuming the token
23393        let use_proc_keyword = self.peek().text.eq_ignore_ascii_case("PROC");
23394        self.expect(TokenType::Procedure)?;
23395
23396        let if_not_exists =
23397            self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists]);
23398        let name = self.parse_table_ref()?;
23399
23400        // Parse parameters (optional parentheses for TSQL)
23401        let (parameters, has_parens) = if self.match_token(TokenType::LParen) {
23402            let params = self.parse_function_parameters()?;
23403            self.expect(TokenType::RParen)?;
23404            (params, true)
23405        } else if self.check(TokenType::Var) && !self.check(TokenType::As) {
23406            // TSQL: CREATE PROCEDURE foo @a INTEGER, @b INTEGER AS ...
23407            // Parameters without parentheses
23408            let params = self.parse_tsql_procedure_params()?;
23409            (params, false)
23410        } else {
23411            (Vec::new(), false)
23412        };
23413
23414        let mut language = None;
23415        let mut security = None;
23416        let mut body = None;
23417        let mut return_type = None;
23418        let mut execute_as = None;
23419        let mut with_options: Vec<String> = Vec::new();
23420
23421        // Parse procedure options
23422        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
23423            if self.match_token(TokenType::Returns) {
23424                // RETURNS type (Snowflake)
23425                return_type = Some(self.parse_data_type()?);
23426            } else if self.match_identifier("EXECUTE") || self.match_token(TokenType::Execute) {
23427                // EXECUTE AS CALLER/OWNER (Snowflake)
23428                if self.match_token(TokenType::As) {
23429                    if self.match_identifier("CALLER") {
23430                        execute_as = Some("CALLER".to_string());
23431                    } else if self.match_identifier("OWNER") {
23432                        execute_as = Some("OWNER".to_string());
23433                    } else if self.match_identifier("SELF") {
23434                        execute_as = Some("SELF".to_string());
23435                    }
23436                }
23437            } else if self.match_token(TokenType::Language) {
23438                // Language can be SQL, PLPGSQL, PYTHON, etc.
23439                language = Some(self.expect_identifier_or_keyword()?);
23440            } else if self.match_identifier("SECURITY") {
23441                if self.match_identifier("DEFINER") {
23442                    security = Some(FunctionSecurity::Definer);
23443                } else if self.match_identifier("INVOKER") {
23444                    security = Some(FunctionSecurity::Invoker);
23445                }
23446            } else if self.match_token(TokenType::With) {
23447                // TSQL: WITH option1, option2, ... AS body
23448                // Options: ENCRYPTION, RECOMPILE, SCHEMABINDING, NATIVE_COMPILATION,
23449                //          EXECUTE AS {OWNER|SELF|CALLER|'username'}
23450                loop {
23451                    if self.match_identifier("EXECUTE") || self.match_token(TokenType::Execute) {
23452                        // EXECUTE AS {OWNER|SELF|CALLER|'username'}
23453                        self.expect(TokenType::As)?;
23454                        if self.check(TokenType::String) {
23455                            let tok = self.advance();
23456                            with_options.push(format!("EXECUTE AS '{}'", tok.text));
23457                        } else {
23458                            let ident = self.expect_identifier_or_keyword()?;
23459                            with_options.push(format!("EXECUTE AS {}", ident.to_ascii_uppercase()));
23460                        }
23461                    } else {
23462                        let opt = self.expect_identifier_or_keyword()?;
23463                        with_options.push(opt.to_ascii_uppercase());
23464                    }
23465                    if !self.match_token(TokenType::Comma) {
23466                        break;
23467                    }
23468                }
23469            } else if self.match_token(TokenType::As) {
23470                // Parse procedure body
23471                if self.check(TokenType::String) {
23472                    // TokenType::String means single-quoted - tokenizer strips quotes
23473                    let tok = self.advance();
23474                    body = Some(FunctionBody::StringLiteral(tok.text.clone()));
23475                } else if self.match_token(TokenType::Begin) {
23476                    // Parse BEGIN ... END block as a list of statements
23477                    let mut statements = Vec::new();
23478                    while !self.check(TokenType::End) && !self.is_at_end() {
23479                        // Skip optional semicolons between statements
23480                        while self.match_token(TokenType::Semicolon) {}
23481                        if self.check(TokenType::End) {
23482                            break;
23483                        }
23484                        statements.push(self.parse_statement()?);
23485                        // Skip optional semicolon after statement
23486                        self.match_token(TokenType::Semicolon);
23487                    }
23488                    self.expect(TokenType::End)?;
23489                    body = Some(FunctionBody::Statements(statements));
23490                } else {
23491                    // TSQL: AS <statement> (e.g., AS SELECT 1)
23492                    let stmt = self.parse_statement()?;
23493                    body = Some(FunctionBody::Expression(stmt));
23494                }
23495            } else {
23496                break;
23497            }
23498        }
23499
23500        Ok(Expression::CreateProcedure(Box::new(CreateProcedure {
23501            name,
23502            parameters,
23503            body,
23504            or_replace,
23505            if_not_exists,
23506            language,
23507            security,
23508            return_type,
23509            execute_as,
23510            with_options,
23511            has_parens,
23512            use_proc_keyword,
23513        })))
23514    }
23515
23516    /// Parse DROP PROCEDURE statement
23517    fn parse_drop_procedure(&mut self) -> Result<Expression> {
23518        self.expect(TokenType::Procedure)?;
23519
23520        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
23521        let name = self.parse_table_ref()?;
23522
23523        let parameters = if self.match_token(TokenType::LParen) {
23524            let mut types = Vec::new();
23525            if !self.check(TokenType::RParen) {
23526                loop {
23527                    types.push(self.parse_data_type()?);
23528                    if !self.match_token(TokenType::Comma) {
23529                        break;
23530                    }
23531                }
23532            }
23533            self.expect(TokenType::RParen)?;
23534            Some(types)
23535        } else {
23536            None
23537        };
23538
23539        let cascade = self.match_token(TokenType::Cascade);
23540        if !cascade {
23541            self.match_token(TokenType::Restrict);
23542        }
23543
23544        Ok(Expression::DropProcedure(Box::new(DropProcedure {
23545            name,
23546            parameters,
23547            if_exists,
23548            cascade,
23549        })))
23550    }
23551
23552    /// Parse CREATE SEQUENCE statement
23553    fn parse_create_sequence(&mut self, temporary: bool, or_replace: bool) -> Result<Expression> {
23554        self.expect(TokenType::Sequence)?;
23555
23556        let if_not_exists =
23557            self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists]);
23558        let name = self.parse_table_ref()?;
23559
23560        let mut seq = CreateSequence {
23561            name,
23562            if_not_exists,
23563            temporary,
23564            or_replace,
23565            as_type: None,
23566            increment: None,
23567            minvalue: None,
23568            maxvalue: None,
23569            start: None,
23570            cache: None,
23571            cycle: false,
23572            owned_by: None,
23573            owned_by_none: false,
23574            order: None,
23575            comment: None,
23576            sharing: None,
23577            scale_modifier: None,
23578            shard_modifier: None,
23579            property_order: Vec::new(),
23580        };
23581
23582        // Parse optional AS <type> clause (e.g., AS SMALLINT, AS BIGINT)
23583        if self.match_token(TokenType::As) {
23584            seq.as_type = Some(self.parse_data_type()?);
23585        }
23586
23587        // Parse sequence options
23588        // Handle optional WITH keyword before options (Snowflake: WITH START = n INCREMENT = n)
23589        self.match_token(TokenType::With);
23590
23591        loop {
23592            // Skip optional commas between options (Snowflake uses comma-separated options)
23593            self.match_token(TokenType::Comma);
23594
23595            if self.is_at_end() || self.check(TokenType::Semicolon) {
23596                break;
23597            }
23598
23599            if self.match_token(TokenType::Increment) || self.match_identifier("INCREMENT") {
23600                self.match_token(TokenType::By);
23601                self.match_token(TokenType::Eq); // Snowflake uses = instead of BY
23602                seq.increment = Some(self.parse_signed_integer()?);
23603                seq.property_order.push(SeqPropKind::Increment);
23604            } else if self.match_token(TokenType::Minvalue) {
23605                seq.minvalue = Some(SequenceBound::Value(self.parse_signed_integer()?));
23606                seq.property_order.push(SeqPropKind::Minvalue);
23607            } else if self.match_keywords(&[TokenType::No, TokenType::Minvalue]) {
23608                seq.minvalue = Some(SequenceBound::None);
23609                seq.property_order.push(SeqPropKind::Minvalue);
23610            } else if self.match_identifier("NOMINVALUE") {
23611                seq.minvalue = Some(SequenceBound::None);
23612                seq.property_order.push(SeqPropKind::NoMinvalueWord);
23613            } else if self.match_token(TokenType::Maxvalue) {
23614                seq.maxvalue = Some(SequenceBound::Value(self.parse_signed_integer()?));
23615                seq.property_order.push(SeqPropKind::Maxvalue);
23616            } else if self.match_keywords(&[TokenType::No, TokenType::Maxvalue]) {
23617                seq.maxvalue = Some(SequenceBound::None);
23618                seq.property_order.push(SeqPropKind::Maxvalue);
23619            } else if self.match_identifier("NOMAXVALUE") {
23620                seq.maxvalue = Some(SequenceBound::None);
23621                seq.property_order.push(SeqPropKind::NoMaxvalueWord);
23622            } else if self.match_token(TokenType::Start) {
23623                self.match_token(TokenType::With);
23624                self.match_token(TokenType::Eq); // Snowflake uses = instead of WITH
23625                seq.start = Some(self.parse_signed_integer()?);
23626                seq.property_order.push(SeqPropKind::Start);
23627            } else if self.match_token(TokenType::Cache) {
23628                seq.cache = Some(self.parse_signed_integer()?);
23629                seq.property_order.push(SeqPropKind::Cache);
23630            } else if self.match_identifier("NOCACHE") {
23631                // Oracle: NOCACHE (single word)
23632                seq.property_order.push(SeqPropKind::NoCacheWord);
23633            } else if self.match_token(TokenType::Cycle) {
23634                seq.cycle = true;
23635                seq.property_order.push(SeqPropKind::Cycle);
23636            } else if self.match_token(TokenType::NoCycle) {
23637                // NOCYCLE keyword token - preserve as single word
23638                seq.cycle = false;
23639                seq.property_order.push(SeqPropKind::NoCycleWord);
23640            } else if self.match_token(TokenType::No) {
23641                // Two-word NO forms
23642                if self.match_token(TokenType::Cycle) {
23643                    seq.cycle = false;
23644                    seq.property_order.push(SeqPropKind::NoCycle);
23645                } else if self.match_token(TokenType::Cache) || self.match_identifier("CACHE") {
23646                    seq.property_order.push(SeqPropKind::NoCache);
23647                } else if self.match_token(TokenType::Minvalue) {
23648                    seq.minvalue = Some(SequenceBound::None);
23649                    seq.property_order.push(SeqPropKind::Minvalue);
23650                } else if self.match_token(TokenType::Maxvalue) {
23651                    seq.maxvalue = Some(SequenceBound::None);
23652                    seq.property_order.push(SeqPropKind::Maxvalue);
23653                } else {
23654                    // Unexpected token after NO
23655                    break;
23656                }
23657            } else if self.match_token(TokenType::Owned) {
23658                self.expect(TokenType::By)?;
23659                if self.match_identifier("NONE") {
23660                    seq.owned_by = None;
23661                    seq.owned_by_none = true;
23662                } else {
23663                    seq.owned_by = Some(self.parse_table_ref()?);
23664                }
23665                seq.property_order.push(SeqPropKind::OwnedBy);
23666            } else if self.match_token(TokenType::Order) {
23667                // Snowflake/Oracle: ORDER option
23668                seq.order = Some(true);
23669                seq.property_order.push(SeqPropKind::Order);
23670            } else if self.match_identifier("NOORDER") {
23671                // Snowflake/Oracle: NOORDER option
23672                seq.order = Some(false);
23673                seq.property_order.push(SeqPropKind::NoOrder);
23674            } else if self.match_token(TokenType::Comment) || self.match_identifier("COMMENT") {
23675                // Snowflake: COMMENT = 'value'
23676                self.expect(TokenType::Eq)?;
23677                let comment_val = self.expect(TokenType::String)?;
23678                seq.comment = Some(comment_val.text.clone());
23679                seq.property_order.push(SeqPropKind::Comment);
23680            } else if self.match_identifier("SHARING") {
23681                // Oracle: SHARING=value
23682                self.expect(TokenType::Eq)?;
23683                let val = self.expect_identifier_or_keyword()?;
23684                seq.sharing = Some(val);
23685                seq.property_order.push(SeqPropKind::Sharing);
23686            } else if self.match_identifier("NOKEEP") {
23687                seq.property_order.push(SeqPropKind::NoKeep);
23688            } else if self.match_token(TokenType::Keep) || self.match_identifier("KEEP") {
23689                seq.property_order.push(SeqPropKind::Keep);
23690            } else if self.match_identifier("SCALE") {
23691                let modifier = if self.match_identifier("EXTEND") {
23692                    "EXTEND".to_string()
23693                } else if self.match_identifier("NOEXTEND") {
23694                    "NOEXTEND".to_string()
23695                } else {
23696                    String::new()
23697                };
23698                seq.scale_modifier = Some(modifier);
23699                seq.property_order.push(SeqPropKind::Scale);
23700            } else if self.match_identifier("NOSCALE") {
23701                seq.property_order.push(SeqPropKind::NoScale);
23702            } else if self.match_identifier("SHARD") {
23703                let modifier = if self.match_identifier("EXTEND") {
23704                    "EXTEND".to_string()
23705                } else if self.match_identifier("NOEXTEND") {
23706                    "NOEXTEND".to_string()
23707                } else {
23708                    String::new()
23709                };
23710                seq.shard_modifier = Some(modifier);
23711                seq.property_order.push(SeqPropKind::Shard);
23712            } else if self.match_identifier("NOSHARD") {
23713                seq.property_order.push(SeqPropKind::NoShard);
23714            } else if self.match_identifier("SESSION") {
23715                seq.property_order.push(SeqPropKind::Session);
23716            } else if self.match_identifier("GLOBAL") {
23717                seq.property_order.push(SeqPropKind::Global);
23718            } else {
23719                break;
23720            }
23721        }
23722
23723        Ok(Expression::CreateSequence(Box::new(seq)))
23724    }
23725
23726    /// Parse a signed integer (positive or negative)
23727    fn parse_signed_integer(&mut self) -> Result<i64> {
23728        let negative = self.match_token(TokenType::Dash);
23729        let tok = self.expect(TokenType::Number)?;
23730        let value: i64 = tok
23731            .text
23732            .parse()
23733            .map_err(|_| self.parse_error(format!("Invalid integer: {}", tok.text)))?;
23734        Ok(if negative { -value } else { value })
23735    }
23736
23737    /// Parse DROP SEQUENCE statement
23738    fn parse_drop_sequence(&mut self) -> Result<Expression> {
23739        self.expect(TokenType::Sequence)?;
23740
23741        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
23742        let name = self.parse_table_ref()?;
23743
23744        let cascade = self.match_token(TokenType::Cascade);
23745        if !cascade {
23746            self.match_token(TokenType::Restrict);
23747        }
23748
23749        Ok(Expression::DropSequence(Box::new(DropSequence {
23750            name,
23751            if_exists,
23752            cascade,
23753        })))
23754    }
23755
23756    /// Parse ALTER SEQUENCE statement
23757    fn parse_alter_sequence(&mut self) -> Result<Expression> {
23758        self.expect(TokenType::Sequence)?;
23759
23760        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
23761        let name = self.parse_table_ref()?;
23762
23763        let mut seq = AlterSequence {
23764            name,
23765            if_exists,
23766            increment: None,
23767            minvalue: None,
23768            maxvalue: None,
23769            start: None,
23770            restart: None,
23771            cache: None,
23772            cycle: None,
23773            owned_by: None,
23774        };
23775
23776        // Parse sequence options
23777        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
23778            if self.match_token(TokenType::Increment) || self.match_identifier("INCREMENT") {
23779                self.match_token(TokenType::By);
23780                seq.increment = Some(self.parse_signed_integer()?);
23781            } else if self.match_token(TokenType::Minvalue) {
23782                seq.minvalue = Some(SequenceBound::Value(self.parse_signed_integer()?));
23783            } else if self.match_keywords(&[TokenType::No, TokenType::Minvalue]) {
23784                seq.minvalue = Some(SequenceBound::None);
23785            } else if self.match_token(TokenType::Maxvalue) {
23786                seq.maxvalue = Some(SequenceBound::Value(self.parse_signed_integer()?));
23787            } else if self.match_keywords(&[TokenType::No, TokenType::Maxvalue]) {
23788                seq.maxvalue = Some(SequenceBound::None);
23789            } else if self.match_token(TokenType::Start) {
23790                self.match_token(TokenType::With);
23791                seq.start = Some(self.parse_signed_integer()?);
23792            } else if self.match_token(TokenType::Restart) {
23793                if self.match_token(TokenType::With)
23794                    || self.check(TokenType::Number)
23795                    || self.check(TokenType::Dash)
23796                {
23797                    seq.restart = Some(Some(self.parse_signed_integer()?));
23798                } else {
23799                    seq.restart = Some(None);
23800                }
23801            } else if self.match_token(TokenType::Cache) {
23802                seq.cache = Some(self.parse_signed_integer()?);
23803            } else if self.match_token(TokenType::Cycle) {
23804                seq.cycle = Some(true);
23805            } else if self.match_token(TokenType::NoCycle) {
23806                seq.cycle = Some(false);
23807            } else if self.match_token(TokenType::Owned) {
23808                self.expect(TokenType::By)?;
23809                if self.match_identifier("NONE") {
23810                    seq.owned_by = Some(None);
23811                } else {
23812                    seq.owned_by = Some(Some(self.parse_table_ref()?));
23813                }
23814            } else {
23815                break;
23816            }
23817        }
23818
23819        Ok(Expression::AlterSequence(Box::new(seq)))
23820    }
23821
23822    /// Parse CREATE TRIGGER statement
23823    fn parse_create_trigger(
23824        &mut self,
23825        or_replace: bool,
23826        constraint: bool,
23827        create_pos: usize,
23828    ) -> Result<Expression> {
23829        self.expect(TokenType::Trigger)?;
23830
23831        let name = self.expect_identifier_with_quoted()?;
23832
23833        // TSQL triggers: CREATE TRIGGER name ON table AFTER INSERT AS BEGIN...END
23834        // These have ON before timing, unlike standard triggers.
23835        // Fall back to Command for these (matches Python sqlglot behavior).
23836        if self.check(TokenType::On) && !constraint {
23837            self.current = create_pos;
23838            return self.fallback_to_command(create_pos);
23839        }
23840
23841        // Parse timing (BEFORE, AFTER, INSTEAD OF)
23842        let timing = if self.match_token(TokenType::Before) {
23843            TriggerTiming::Before
23844        } else if self.match_token(TokenType::After) {
23845            TriggerTiming::After
23846        } else if self.match_token(TokenType::Instead) {
23847            self.expect(TokenType::Of)?;
23848            TriggerTiming::InsteadOf
23849        } else {
23850            // Fall back to Command for unknown trigger syntax
23851            self.current = create_pos;
23852            return self.fallback_to_command(create_pos);
23853        };
23854
23855        // Parse events
23856        let mut events = Vec::new();
23857        loop {
23858            if self.match_token(TokenType::Insert) {
23859                events.push(TriggerEvent::Insert);
23860            } else if self.match_token(TokenType::Update) {
23861                if self.match_token(TokenType::Of) {
23862                    let mut cols = Vec::new();
23863                    loop {
23864                        cols.push(Identifier::new(self.expect_identifier()?));
23865                        if !self.match_token(TokenType::Comma) {
23866                            break;
23867                        }
23868                    }
23869                    events.push(TriggerEvent::Update(Some(cols)));
23870                } else {
23871                    events.push(TriggerEvent::Update(None));
23872                }
23873            } else if self.match_token(TokenType::Delete) {
23874                events.push(TriggerEvent::Delete);
23875            } else if self.match_token(TokenType::Truncate) {
23876                events.push(TriggerEvent::Truncate);
23877            } else {
23878                break;
23879            }
23880
23881            if !self.match_token(TokenType::Or) {
23882                break;
23883            }
23884        }
23885
23886        self.expect(TokenType::On)?;
23887        let table = self.parse_table_ref()?;
23888
23889        // Parse optional REFERENCING clause (for non-constraint triggers)
23890        let referencing = if !constraint && self.match_token(TokenType::Referencing) {
23891            let mut ref_clause = TriggerReferencing {
23892                old_table: None,
23893                new_table: None,
23894                old_row: None,
23895                new_row: None,
23896            };
23897            while self.match_token(TokenType::Old) || self.match_token(TokenType::New) {
23898                let is_old = self.previous().token_type == TokenType::Old;
23899                let is_table = self.match_token(TokenType::Table);
23900                let _is_row = !is_table && self.match_token(TokenType::Row);
23901                self.match_token(TokenType::As);
23902                let alias = Identifier::new(self.expect_identifier()?);
23903
23904                if is_old {
23905                    if is_table {
23906                        ref_clause.old_table = Some(alias);
23907                    } else {
23908                        ref_clause.old_row = Some(alias);
23909                    }
23910                } else {
23911                    if is_table {
23912                        ref_clause.new_table = Some(alias);
23913                    } else {
23914                        ref_clause.new_row = Some(alias);
23915                    }
23916                }
23917            }
23918            Some(ref_clause)
23919        } else {
23920            None
23921        };
23922
23923        // Parse deferrable options for constraint triggers (comes before FOR EACH ROW in PostgreSQL)
23924        let mut deferrable = None;
23925        let mut initially_deferred = None;
23926        if constraint {
23927            if self.match_identifier("DEFERRABLE") {
23928                deferrable = Some(true);
23929            } else if self.match_keywords(&[TokenType::Not, TokenType::Identifier]) {
23930                // NOT DEFERRABLE
23931                deferrable = Some(false);
23932            }
23933            if self.match_identifier("INITIALLY") {
23934                if self.match_identifier("DEFERRED") {
23935                    initially_deferred = Some(true);
23936                } else if self.match_identifier("IMMEDIATE") {
23937                    initially_deferred = Some(false);
23938                }
23939            }
23940        }
23941
23942        // Parse FOR EACH ROW/STATEMENT (optional)
23943        let for_each = if self.match_token(TokenType::For) {
23944            self.match_token(TokenType::Each);
23945            if self.match_token(TokenType::Row) {
23946                Some(TriggerForEach::Row)
23947            } else if self.match_token(TokenType::Statement) {
23948                Some(TriggerForEach::Statement)
23949            } else {
23950                Some(TriggerForEach::Row)
23951            }
23952        } else {
23953            None
23954        };
23955
23956        // Parse optional WHEN clause (parentheses are optional, e.g. SQLite)
23957        let (when, when_paren) = if self.match_token(TokenType::When) {
23958            let has_paren = self.match_token(TokenType::LParen);
23959            let expr = self.parse_expression()?;
23960            if has_paren {
23961                self.expect(TokenType::RParen)?;
23962            }
23963            (Some(expr), has_paren)
23964        } else {
23965            (None, false)
23966        };
23967
23968        // Parse trigger body
23969        let body = if self.match_token(TokenType::Execute) {
23970            self.match_token(TokenType::Function);
23971            self.match_token(TokenType::Procedure);
23972            let func_name = self.parse_table_ref()?;
23973            self.expect(TokenType::LParen)?;
23974            let mut args = Vec::new();
23975            if !self.check(TokenType::RParen) {
23976                loop {
23977                    args.push(self.parse_expression()?);
23978                    if !self.match_token(TokenType::Comma) {
23979                        break;
23980                    }
23981                }
23982            }
23983            self.expect(TokenType::RParen)?;
23984            TriggerBody::Execute {
23985                function: func_name,
23986                args,
23987            }
23988        } else if self.match_token(TokenType::Begin) {
23989            // Record start position (first token after BEGIN)
23990            let body_start = if !self.is_at_end() {
23991                self.tokens[self.current].span.start
23992            } else {
23993                0
23994            };
23995            let mut depth = 1;
23996            while depth > 0 && !self.is_at_end() {
23997                let tok = self.advance();
23998                if tok.token_type == TokenType::Begin {
23999                    depth += 1;
24000                } else if tok.token_type == TokenType::End {
24001                    depth -= 1;
24002                    if depth == 0 {
24003                        break;
24004                    }
24005                }
24006            }
24007            // Extract verbatim text from source if available
24008            let block_content = if let Some(ref source) = self.source {
24009                // End position is the start of the END token
24010                let body_end = if self.current > 0 {
24011                    self.tokens[self.current - 1].span.start
24012                } else {
24013                    body_start
24014                };
24015                source[body_start..body_end].trim().to_string()
24016            } else {
24017                // Fallback: no source available
24018                String::new()
24019            };
24020            TriggerBody::Block(block_content)
24021        } else {
24022            return Err(self.parse_error("Expected EXECUTE or BEGIN in trigger body"));
24023        };
24024
24025        Ok(Expression::CreateTrigger(Box::new(CreateTrigger {
24026            name,
24027            table,
24028            timing,
24029            events,
24030            for_each,
24031            when,
24032            when_paren,
24033            body,
24034            or_replace,
24035            constraint,
24036            deferrable,
24037            initially_deferred,
24038            referencing,
24039        })))
24040    }
24041
24042    /// Parse DROP TRIGGER statement
24043    fn parse_drop_trigger(&mut self) -> Result<Expression> {
24044        self.expect(TokenType::Trigger)?;
24045
24046        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
24047        let name = Identifier::new(self.expect_identifier()?);
24048
24049        let table = if self.match_token(TokenType::On) {
24050            Some(self.parse_table_ref()?)
24051        } else {
24052            None
24053        };
24054
24055        let cascade = self.match_token(TokenType::Cascade);
24056        if !cascade {
24057            self.match_token(TokenType::Restrict);
24058        }
24059
24060        Ok(Expression::DropTrigger(Box::new(DropTrigger {
24061            name,
24062            table,
24063            if_exists,
24064            cascade,
24065        })))
24066    }
24067
24068    /// Parse CREATE TYPE statement
24069    fn parse_create_type(&mut self) -> Result<Expression> {
24070        self.expect(TokenType::Type)?;
24071
24072        let if_not_exists =
24073            self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists]);
24074        let name = self.parse_table_ref()?;
24075
24076        self.expect(TokenType::As)?;
24077
24078        let definition = if self.match_token(TokenType::Enum) {
24079            // ENUM type
24080            self.expect(TokenType::LParen)?;
24081            let mut values = Vec::new();
24082            loop {
24083                let tok = self.expect(TokenType::String)?;
24084                values.push(tok.text.trim_matches('\'').to_string());
24085                if !self.match_token(TokenType::Comma) {
24086                    break;
24087                }
24088            }
24089            self.expect(TokenType::RParen)?;
24090            TypeDefinition::Enum(values)
24091        } else if self.match_token(TokenType::LParen) {
24092            // Composite type
24093            let mut attrs = Vec::new();
24094            loop {
24095                let attr_name = Identifier::new(self.expect_identifier()?);
24096                let data_type = self.parse_data_type()?;
24097                let collate = if self.match_identifier("COLLATE") {
24098                    Some(Identifier::new(self.expect_identifier()?))
24099                } else {
24100                    None
24101                };
24102                attrs.push(TypeAttribute {
24103                    name: attr_name,
24104                    data_type,
24105                    collate,
24106                });
24107                if !self.match_token(TokenType::Comma) {
24108                    break;
24109                }
24110            }
24111            self.expect(TokenType::RParen)?;
24112            TypeDefinition::Composite(attrs)
24113        } else if self.match_token(TokenType::Range) {
24114            // Range type
24115            self.expect(TokenType::LParen)?;
24116            self.match_identifier("SUBTYPE");
24117            self.match_token(TokenType::Eq);
24118            let subtype = self.parse_data_type()?;
24119
24120            let mut subtype_diff = None;
24121            let mut canonical = None;
24122
24123            while self.match_token(TokenType::Comma) {
24124                if self.match_identifier("SUBTYPE_DIFF") {
24125                    self.match_token(TokenType::Eq);
24126                    subtype_diff = Some(self.expect_identifier()?);
24127                } else if self.match_identifier("CANONICAL") {
24128                    self.match_token(TokenType::Eq);
24129                    canonical = Some(self.expect_identifier()?);
24130                }
24131            }
24132            self.expect(TokenType::RParen)?;
24133
24134            TypeDefinition::Range {
24135                subtype,
24136                subtype_diff,
24137                canonical,
24138            }
24139        } else {
24140            return Err(
24141                self.parse_error("Expected ENUM, composite type definition, or RANGE after AS")
24142            );
24143        };
24144
24145        Ok(Expression::CreateType(Box::new(CreateType {
24146            name,
24147            definition,
24148            if_not_exists,
24149        })))
24150    }
24151
24152    /// Parse CREATE DOMAIN statement
24153    fn parse_create_domain(&mut self) -> Result<Expression> {
24154        self.expect(TokenType::Domain)?;
24155
24156        let if_not_exists =
24157            self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists]);
24158        let name = self.parse_table_ref()?;
24159
24160        self.expect(TokenType::As)?;
24161        let base_type = self.parse_data_type()?;
24162
24163        let mut default = None;
24164        let mut constraints = Vec::new();
24165
24166        // Parse domain options
24167        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
24168            if self.match_token(TokenType::Default) {
24169                default = Some(self.parse_expression()?);
24170            } else if self.match_token(TokenType::Constraint) {
24171                let constr_name = Some(Identifier::new(self.expect_identifier()?));
24172                self.expect(TokenType::Check)?;
24173                self.expect(TokenType::LParen)?;
24174                let check_expr = self.parse_expression()?;
24175                self.expect(TokenType::RParen)?;
24176                constraints.push(DomainConstraint {
24177                    name: constr_name,
24178                    check: check_expr,
24179                });
24180            } else if self.match_token(TokenType::Check) {
24181                self.expect(TokenType::LParen)?;
24182                let check_expr = self.parse_expression()?;
24183                self.expect(TokenType::RParen)?;
24184                constraints.push(DomainConstraint {
24185                    name: None,
24186                    check: check_expr,
24187                });
24188            } else if self.match_keywords(&[TokenType::Not, TokenType::Null]) {
24189                // NOT NULL is a constraint - represented as VALUE IS NOT NULL
24190                constraints.push(DomainConstraint {
24191                    name: None,
24192                    check: Expression::IsNull(Box::new(IsNull {
24193                        this: Expression::Identifier(Identifier::new("VALUE")),
24194                        not: true,
24195                        postfix_form: false,
24196                    })),
24197                });
24198            } else {
24199                break;
24200            }
24201        }
24202
24203        Ok(Expression::CreateType(Box::new(CreateType {
24204            name,
24205            definition: TypeDefinition::Domain {
24206                base_type,
24207                default,
24208                constraints,
24209            },
24210            if_not_exists,
24211        })))
24212    }
24213
24214    /// Parse CREATE STAGE statement (Snowflake)
24215    fn parse_create_stage(&mut self, or_replace: bool, temporary: bool) -> Result<Expression> {
24216        self.skip(); // consume STAGE (identifier)
24217                        // Parse remaining tokens, normalizing FILE_FORMAT clause
24218        let start = self.current;
24219        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
24220            self.skip();
24221        }
24222        let sql = self.tokens_to_sql_stage_format(start, self.current);
24223
24224        // Build the CREATE prefix with modifiers
24225        let mut prefix = String::from("CREATE");
24226        if or_replace {
24227            prefix.push_str(" OR REPLACE");
24228        }
24229        if temporary {
24230            prefix.push_str(" TEMPORARY");
24231        }
24232        prefix.push_str(" STAGE");
24233
24234        Ok(Expression::Raw(Raw {
24235            sql: format!("{} {}", prefix, sql),
24236        }))
24237    }
24238
24239    /// Parse CREATE TAG statement (Snowflake)
24240    fn parse_create_tag(&mut self, or_replace: bool) -> Result<Expression> {
24241        self.skip(); // consume TAG
24242                        // Capture remaining tokens as raw SQL
24243        let start = self.current;
24244        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
24245            self.skip();
24246        }
24247        let sql = self.tokens_to_sql(start, self.current);
24248        let prefix = if or_replace {
24249            "CREATE OR REPLACE TAG"
24250        } else {
24251            "CREATE TAG"
24252        };
24253        Ok(Expression::Raw(Raw {
24254            sql: format!("{} {}", prefix, sql),
24255        }))
24256    }
24257
24258    /// Parse CREATE STREAM statement (Snowflake)
24259    fn parse_create_stream(&mut self, _or_replace: bool) -> Result<Expression> {
24260        self.skip(); // consume STREAM
24261                        // Capture remaining tokens as raw SQL
24262        let start = self.current;
24263        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
24264            self.skip();
24265        }
24266        let sql = self.tokens_to_sql(start, self.current);
24267        Ok(Expression::Raw(Raw {
24268            sql: format!("CREATE STREAM {}", sql),
24269        }))
24270    }
24271
24272    /// Parse CREATE FILE FORMAT statement (Snowflake)
24273    fn parse_create_file_format(
24274        &mut self,
24275        or_replace: bool,
24276        temporary: bool,
24277    ) -> Result<Expression> {
24278        self.skip(); // consume FILE
24279        self.skip(); // consume FORMAT
24280                        // Capture remaining tokens as raw SQL
24281        let start = self.current;
24282        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
24283            self.skip();
24284        }
24285        let sql = self.tokens_to_sql(start, self.current);
24286        let mut prefix = String::from("CREATE");
24287        if or_replace {
24288            prefix.push_str(" OR REPLACE");
24289        }
24290        if temporary {
24291            prefix.push_str(" TEMPORARY");
24292        }
24293        prefix.push_str(" FILE FORMAT ");
24294        prefix.push_str(&sql);
24295        Ok(Expression::Raw(Raw { sql: prefix }))
24296    }
24297
24298    /// Parse DROP TYPE statement
24299    fn parse_drop_type(&mut self) -> Result<Expression> {
24300        self.expect(TokenType::Type)?;
24301
24302        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
24303        let name = self.parse_table_ref()?;
24304
24305        let cascade = self.match_token(TokenType::Cascade);
24306        if !cascade {
24307            self.match_token(TokenType::Restrict);
24308        }
24309
24310        Ok(Expression::DropType(Box::new(DropType {
24311            name,
24312            if_exists,
24313            cascade,
24314        })))
24315    }
24316
24317    fn parse_alter_view_with_modifiers(
24318        &mut self,
24319        algorithm: Option<String>,
24320        definer: Option<String>,
24321        sql_security: Option<String>,
24322    ) -> Result<Expression> {
24323        self.expect(TokenType::View)?;
24324
24325        let name = self.parse_table_ref()?;
24326        let mut actions = Vec::new();
24327
24328        // Hive: Optional column aliases with optional COMMENT: (c1, c2) or (c1 COMMENT 'text', c2)
24329        // Only parse if we see LParen followed by identifier (not SELECT for subquery)
24330        let columns = if self.check(TokenType::LParen) {
24331            // Peek ahead to see if this looks like column aliases
24332            let saved = self.current;
24333            self.skip(); // consume LParen
24334
24335            // Check if this is an identifier (column name) vs SELECT keyword
24336            let is_column_aliases = self.check(TokenType::Identifier)
24337                || self.check(TokenType::Var)
24338                || self.check(TokenType::QuotedIdentifier);
24339
24340            if is_column_aliases {
24341                // Parse column aliases
24342                let mut cols = Vec::new();
24343                loop {
24344                    let col_name = self.expect_identifier()?;
24345                    // Optional COMMENT 'text'
24346                    let comment = if self.match_token(TokenType::Comment) {
24347                        Some(self.expect_string()?)
24348                    } else {
24349                        None
24350                    };
24351                    cols.push(ViewColumn {
24352                        name: Identifier::new(col_name),
24353                        comment,
24354                        options: Vec::new(),
24355                    });
24356                    if !self.match_token(TokenType::Comma) {
24357                        break;
24358                    }
24359                }
24360                self.expect(TokenType::RParen)?;
24361                cols
24362            } else {
24363                self.current = saved; // retreat
24364                Vec::new()
24365            }
24366        } else {
24367            Vec::new()
24368        };
24369
24370        // TSQL: WITH option (SCHEMABINDING, ENCRYPTION, VIEW_METADATA) before AS
24371        let with_option = if self.match_token(TokenType::With) {
24372            let opt = self.expect_identifier_or_keyword()?;
24373            Some(opt.to_ascii_uppercase())
24374        } else {
24375            None
24376        };
24377
24378        // Parse actions
24379        if self.match_token(TokenType::Rename) {
24380            self.expect(TokenType::To)?;
24381            actions.push(AlterViewAction::Rename(self.parse_table_ref()?));
24382        } else if self.match_identifier("OWNER") {
24383            self.expect(TokenType::To)?;
24384            actions.push(AlterViewAction::OwnerTo(Identifier::new(
24385                self.expect_identifier()?,
24386            )));
24387        } else if self.match_token(TokenType::Set) {
24388            // Hive: SET TBLPROPERTIES ('key'='value', ...) or SET SCHEMA name
24389            // Trino: SET AUTHORIZATION [ROLE] user
24390            if self.match_identifier("TBLPROPERTIES") {
24391                let props = self.parse_tblproperties_key_value_list()?;
24392                actions.push(AlterViewAction::SetTblproperties(props));
24393            } else if self.match_token(TokenType::Authorization) {
24394                let mut auth_text = String::new();
24395                if self.match_texts(&["ROLE"]) {
24396                    auth_text.push_str("ROLE ");
24397                }
24398                let user = self.expect_identifier()?;
24399                auth_text.push_str(&user);
24400                actions.push(AlterViewAction::SetAuthorization(auth_text));
24401            } else {
24402                self.expect(TokenType::Schema)?;
24403                actions.push(AlterViewAction::SetSchema(Identifier::new(
24404                    self.expect_identifier()?,
24405                )));
24406            }
24407        } else if self.match_identifier("UNSET") {
24408            // Hive: UNSET TBLPROPERTIES ('key1', 'key2', ...)
24409            if !self.match_identifier("TBLPROPERTIES") {
24410                return Err(self.parse_error("Expected TBLPROPERTIES after UNSET"));
24411            }
24412            let keys = self.parse_tblproperties_key_list()?;
24413            actions.push(AlterViewAction::UnsetTblproperties(keys));
24414        } else if self.match_token(TokenType::Alter) {
24415            self.match_token(TokenType::Column);
24416            let col_name = Identifier::new(self.expect_identifier()?);
24417            let action = self.parse_alter_column_action()?;
24418            actions.push(AlterViewAction::AlterColumn {
24419                name: col_name,
24420                action,
24421            });
24422        } else if self.match_token(TokenType::As) {
24423            // AS SELECT ... or AS SELECT ... UNION ... (redefine view query)
24424            let query = self.parse_statement()?;
24425            actions.push(AlterViewAction::AsSelect(Box::new(query)));
24426        }
24427
24428        Ok(Expression::AlterView(Box::new(AlterView {
24429            name,
24430            actions,
24431            algorithm,
24432            definer,
24433            sql_security,
24434            with_option,
24435            columns,
24436        })))
24437    }
24438
24439    /// Parse TBLPROPERTIES key-value list: ('key1'='value1', 'key2'='value2', ...)
24440    fn parse_tblproperties_key_value_list(&mut self) -> Result<Vec<(String, String)>> {
24441        self.expect(TokenType::LParen)?;
24442        let mut props = Vec::new();
24443        loop {
24444            let key = self.expect_string()?;
24445            self.expect(TokenType::Eq)?;
24446            let value = self.expect_string()?;
24447            props.push((key, value));
24448            if !self.match_token(TokenType::Comma) {
24449                break;
24450            }
24451        }
24452        self.expect(TokenType::RParen)?;
24453        Ok(props)
24454    }
24455
24456    /// Parse TBLPROPERTIES key list (for UNSET): ('key1', 'key2', ...)
24457    fn parse_tblproperties_key_list(&mut self) -> Result<Vec<String>> {
24458        self.expect(TokenType::LParen)?;
24459        let mut keys = Vec::new();
24460        loop {
24461            let key = self.expect_string()?;
24462            keys.push(key);
24463            if !self.match_token(TokenType::Comma) {
24464                break;
24465            }
24466        }
24467        self.expect(TokenType::RParen)?;
24468        Ok(keys)
24469    }
24470
24471    /// Parse ALTER INDEX statement
24472    fn parse_alter_index(&mut self) -> Result<Expression> {
24473        self.expect(TokenType::Index)?;
24474
24475        // Use expect_identifier_or_keyword_with_quoted to preserve quoted flag
24476        let name = self.expect_identifier_or_keyword_with_quoted()?;
24477
24478        let table = if self.match_token(TokenType::On) {
24479            Some(self.parse_table_ref()?)
24480        } else {
24481            None
24482        };
24483
24484        let mut actions = Vec::new();
24485
24486        // Parse actions
24487        if self.match_token(TokenType::Rename) {
24488            self.expect(TokenType::To)?;
24489            // Also preserve quoted flag for the new name
24490            actions.push(AlterIndexAction::Rename(
24491                self.expect_identifier_or_keyword_with_quoted()?,
24492            ));
24493        } else if self.match_token(TokenType::Set) {
24494            self.match_identifier("TABLESPACE");
24495            actions.push(AlterIndexAction::SetTablespace(
24496                self.expect_identifier_or_keyword_with_quoted()?,
24497            ));
24498        } else if self.match_identifier("VISIBLE") {
24499            actions.push(AlterIndexAction::Visible(true));
24500        } else if self.match_identifier("INVISIBLE") {
24501            actions.push(AlterIndexAction::Visible(false));
24502        }
24503
24504        Ok(Expression::AlterIndex(Box::new(AlterIndex {
24505            name,
24506            table,
24507            actions,
24508        })))
24509    }
24510
24511    // ==================== End DDL Parsing ====================
24512
24513    /// Parse an expression (with precedence)
24514    /// Assignment (:=) has lower precedence than OR, matching Python sqlglot's
24515    /// _parse_expression -> _parse_assignment -> _parse_disjunction chain
24516    fn parse_expression(&mut self) -> Result<Expression> {
24517        let mut left = self.parse_or()?;
24518
24519        // Handle := assignment operator (MySQL @var := val, DuckDB named args/settings)
24520        // This has lower precedence than OR
24521        while self.match_token(TokenType::ColonEq) {
24522            let right = self.parse_or()?;
24523            left = Expression::PropertyEQ(Box::new(BinaryOp::new(left, right)));
24524        }
24525
24526        // ClickHouse ternary operator: condition ? true_value : false_value
24527        // Parsed as: CASE WHEN condition THEN true_value ELSE false_value END
24528        if matches!(
24529            self.config.dialect,
24530            Some(crate::dialects::DialectType::ClickHouse)
24531        ) && self.match_token(TokenType::Parameter)
24532        {
24533            if self.check(TokenType::Colon) {
24534                return Err(
24535                    self.parse_error("Expected true expression after ? in ClickHouse ternary")
24536                );
24537            }
24538            let true_value = self.parse_or()?;
24539            let false_value = if self.match_token(TokenType::Colon) {
24540                self.parse_or()?
24541            } else {
24542                Expression::Null(Null)
24543            };
24544            left = Expression::IfFunc(Box::new(IfFunc {
24545                original_name: None,
24546                condition: left,
24547                true_value,
24548                false_value: Some(false_value),
24549                inferred_type: None,
24550            }));
24551        }
24552
24553        // ClickHouse: APPLY(func) column transformer
24554        // e.g., COLUMNS('pattern') APPLY(toString) APPLY(length)
24555        // Also: APPLY func (no parens), APPLY(x -> expr) (lambda)
24556        // Only match APPLY when followed by ( — bare APPLY without ( is treated as an alias
24557        // by the select expression parser (e.g., SELECT col apply -> SELECT col AS apply)
24558        if matches!(
24559            self.config.dialect,
24560            Some(crate::dialects::DialectType::ClickHouse)
24561        ) {
24562            while self.check(TokenType::Apply) && self.check_next(TokenType::LParen) {
24563                self.skip(); // consume APPLY
24564                self.skip(); // consume (
24565                let expr = self.parse_expression()?;
24566                self.expect(TokenType::RParen)?;
24567                left = Expression::Apply(Box::new(crate::expressions::Apply {
24568                    this: Box::new(left),
24569                    expression: Box::new(expr),
24570                }));
24571            }
24572        }
24573
24574        Ok(left)
24575    }
24576
24577    /// Parse OR expressions
24578    fn parse_or(&mut self) -> Result<Expression> {
24579        let mut left = self.parse_xor()?;
24580
24581        while self.check(TokenType::Or)
24582            || (self.dpipe_is_logical_or() && self.check(TokenType::DPipe))
24583        {
24584            let mut all_comments = self.previous_trailing_comments().to_vec();
24585            // Also capture leading comments on the OR token (comments on a separate line before OR)
24586            all_comments.extend_from_slice(self.current_leading_comments());
24587            self.skip(); // consume OR
24588            all_comments.extend_from_slice(self.previous_trailing_comments());
24589            // Clear trailing_comments from left expression to avoid duplication
24590            if !all_comments.is_empty() {
24591                Self::clear_rightmost_trailing_comments(&mut left);
24592            }
24593            // Filter out empty/whitespace-only comments
24594            all_comments.retain(|c| !c.trim().is_empty());
24595            // Split: block comments go before operator, line comments go after
24596            let mut left_comments = Vec::new();
24597            let mut operator_comments = Vec::new();
24598            for comment in all_comments {
24599                if comment.starts_with("/*") {
24600                    left_comments.push(comment);
24601                } else {
24602                    operator_comments.push(comment);
24603                }
24604            }
24605            let mut right = self.parse_xor()?;
24606            // If parse_comparison stored pending leading comments, attach them
24607            if !self.pending_leading_comments.is_empty() {
24608                let pending = std::mem::take(&mut self.pending_leading_comments);
24609                right = Expression::Annotated(Box::new(Annotated {
24610                    this: right,
24611                    trailing_comments: pending,
24612                }));
24613            }
24614            left = Expression::Or(Box::new(BinaryOp {
24615                left,
24616                right,
24617                left_comments,
24618                operator_comments,
24619                trailing_comments: Vec::new(),
24620                inferred_type: None,
24621            }));
24622        }
24623
24624        Ok(Self::maybe_rebalance_boolean_chain(left, false))
24625    }
24626
24627    /// Whether `||` should be parsed as logical OR for the active dialect.
24628    fn dpipe_is_logical_or(&self) -> bool {
24629        matches!(
24630            self.config.dialect,
24631            Some(crate::dialects::DialectType::MySQL | crate::dialects::DialectType::Solr)
24632        )
24633    }
24634
24635    /// Parse XOR expressions (MySQL logical XOR)
24636    fn parse_xor(&mut self) -> Result<Expression> {
24637        let mut left = self.parse_and()?;
24638
24639        while self.match_token(TokenType::Xor) {
24640            let right = self.parse_and()?;
24641            left = Expression::Xor(Box::new(Xor {
24642                this: Some(Box::new(left)),
24643                expression: Some(Box::new(right)),
24644                expressions: Vec::new(),
24645            }));
24646        }
24647
24648        Ok(left)
24649    }
24650
24651    /// Parse AND expressions
24652    fn parse_and(&mut self) -> Result<Expression> {
24653        let mut left = self.parse_not()?;
24654
24655        while self.check(TokenType::And) {
24656            // Capture comments from the token before AND (left operand's last token)
24657            let mut all_comments = self.previous_trailing_comments().to_vec();
24658            // Also capture leading comments on the AND token (comments on a separate line before AND)
24659            all_comments.extend_from_slice(self.current_leading_comments());
24660            self.skip(); // consume AND
24661                            // Also capture any trailing comments on the AND token itself
24662            all_comments.extend_from_slice(self.previous_trailing_comments());
24663            // Clear trailing_comments from left expression to avoid duplication
24664            if !all_comments.is_empty() {
24665                Self::clear_rightmost_trailing_comments(&mut left);
24666            }
24667            // Filter out empty/whitespace-only comments (e.g., bare "--" with no content)
24668            all_comments.retain(|c| !c.trim().is_empty());
24669            // Split comments: block comments (/*...*/) go BEFORE the operator (left_comments),
24670            // line comments (raw text from --) go AFTER the operator (operator_comments).
24671            // This matches Python sqlglot's behavior where inline block comments stay
24672            // in-place and line comments shift to after the operator.
24673            let mut left_comments = Vec::new();
24674            let mut operator_comments = Vec::new();
24675            for comment in all_comments {
24676                if comment.starts_with("/*") {
24677                    left_comments.push(comment);
24678                } else {
24679                    operator_comments.push(comment);
24680                }
24681            }
24682            let mut right = self.parse_not()?;
24683            // If parse_comparison stored pending leading comments (comments before
24684            // the right operand's first token with no comparison following),
24685            // attach them as trailing_comments on the right expression.
24686            if !self.pending_leading_comments.is_empty() {
24687                let pending = std::mem::take(&mut self.pending_leading_comments);
24688                right = Expression::Annotated(Box::new(Annotated {
24689                    this: right,
24690                    trailing_comments: pending,
24691                }));
24692            }
24693            left = Expression::And(Box::new(BinaryOp {
24694                left,
24695                right,
24696                left_comments,
24697                operator_comments,
24698                trailing_comments: Vec::new(),
24699                inferred_type: None,
24700            }));
24701        }
24702
24703        Ok(Self::maybe_rebalance_boolean_chain(left, true))
24704    }
24705
24706    /// Rebalance AND/OR chains into a balanced tree when no connector comments are present.
24707    /// This keeps connector chain depth logarithmic for very large predicates.
24708    fn maybe_rebalance_boolean_chain(expr: Expression, is_and: bool) -> Expression {
24709        if !Self::should_rebalance_boolean_chain(&expr, is_and) {
24710            return expr;
24711        }
24712
24713        let terms = Self::flatten_boolean_terms_owned(expr, is_and);
24714        if terms.len() <= 2 {
24715            return Self::build_balanced_boolean_tree(terms, is_and);
24716        }
24717
24718        Self::build_balanced_boolean_tree(terms, is_and)
24719    }
24720
24721    fn should_rebalance_boolean_chain(expr: &Expression, is_and: bool) -> bool {
24722        let mut leaf_count = 0usize;
24723        let mut stack = vec![expr];
24724
24725        while let Some(node) = stack.pop() {
24726            match (is_and, node) {
24727                (true, Expression::And(op)) => {
24728                    if !op.left_comments.is_empty()
24729                        || !op.operator_comments.is_empty()
24730                        || !op.trailing_comments.is_empty()
24731                    {
24732                        return false;
24733                    }
24734                    stack.push(&op.right);
24735                    stack.push(&op.left);
24736                }
24737                (false, Expression::Or(op)) => {
24738                    if !op.left_comments.is_empty()
24739                        || !op.operator_comments.is_empty()
24740                        || !op.trailing_comments.is_empty()
24741                    {
24742                        return false;
24743                    }
24744                    stack.push(&op.right);
24745                    stack.push(&op.left);
24746                }
24747                _ => leaf_count += 1,
24748            }
24749        }
24750
24751        leaf_count > 2
24752    }
24753
24754    fn flatten_boolean_terms_owned(expr: Expression, is_and: bool) -> Vec<Expression> {
24755        let mut terms = Vec::new();
24756        let mut stack = vec![expr];
24757
24758        while let Some(node) = stack.pop() {
24759            match (is_and, node) {
24760                (true, Expression::And(op)) => {
24761                    stack.push(op.right);
24762                    stack.push(op.left);
24763                }
24764                (false, Expression::Or(op)) => {
24765                    stack.push(op.right);
24766                    stack.push(op.left);
24767                }
24768                (_, other) => terms.push(other),
24769            }
24770        }
24771
24772        terms
24773    }
24774
24775    fn build_balanced_boolean_tree(mut terms: Vec<Expression>, is_and: bool) -> Expression {
24776        if terms.is_empty() {
24777            return Expression::Null(Null);
24778        }
24779
24780        while terms.len() > 1 {
24781            let mut next = Vec::with_capacity((terms.len() + 1) / 2);
24782            let mut iter = terms.into_iter();
24783
24784            while let Some(left) = iter.next() {
24785                if let Some(right) = iter.next() {
24786                    let combined = if is_and {
24787                        Expression::And(Box::new(BinaryOp::new(left, right)))
24788                    } else {
24789                        Expression::Or(Box::new(BinaryOp::new(left, right)))
24790                    };
24791                    next.push(combined);
24792                } else {
24793                    next.push(left);
24794                }
24795            }
24796
24797            terms = next;
24798        }
24799
24800        terms.pop().unwrap_or(Expression::Null(Null))
24801    }
24802
24803    /// Parse NOT expressions
24804    fn parse_not(&mut self) -> Result<Expression> {
24805        if self.match_token(TokenType::Not) {
24806            let expr = self.parse_not()?;
24807            Ok(Expression::Not(Box::new(UnaryOp::new(expr))))
24808        } else {
24809            self.parse_comparison()
24810        }
24811    }
24812
24813    /// Parse comparison expressions
24814    fn parse_comparison(&mut self) -> Result<Expression> {
24815        // Capture leading comments from the first token before parsing the left side.
24816        // If a comparison operator follows, these are placed after the left operand.
24817        let pre_left_comments = self.current_leading_comments().to_vec();
24818        let mut left = self.parse_bitwise_or()?;
24819
24820        // Only attach pre-left comments when a comparison operator follows.
24821        // When no comparison follows (e.g., in SELECT list expressions or AND operands),
24822        // the comments are returned to the caller by being accessible via the
24823        // `comparison_pre_left_comments` field, so they can be placed appropriately
24824        // (e.g., after an alias name, or after the expression in an AND chain).
24825        let has_comparison_op = !self.is_at_end() && matches!(
24826            self.peek().token_type,
24827            TokenType::Eq | TokenType::Neq | TokenType::Lt | TokenType::Gt
24828            | TokenType::Lte | TokenType::Gte | TokenType::Is | TokenType::In
24829            | TokenType::Not | TokenType::Between | TokenType::Like
24830            | TokenType::ILike | TokenType::RLike | TokenType::SimilarTo
24831        );
24832
24833        if !pre_left_comments.is_empty() {
24834            if has_comparison_op {
24835                // Comparison follows: attach comments between left operand and operator
24836                match &mut left {
24837                    Expression::Column(col) => {
24838                        col.trailing_comments.extend(pre_left_comments);
24839                    }
24840                    Expression::Identifier(id) => {
24841                        id.trailing_comments.extend(pre_left_comments);
24842                    }
24843                    _ => {
24844                        left = Expression::Annotated(Box::new(Annotated {
24845                            this: left,
24846                            trailing_comments: pre_left_comments,
24847                        }));
24848                    }
24849                }
24850            } else {
24851                // No comparison operator: store comments for the caller to use.
24852                // Save them as "pending" comments that the caller can retrieve.
24853                self.pending_leading_comments = pre_left_comments;
24854            }
24855        }
24856
24857        loop {
24858            let mut global_in = false;
24859            if matches!(
24860                self.config.dialect,
24861                Some(crate::dialects::DialectType::ClickHouse)
24862            ) && self.check_identifier("GLOBAL")
24863                && (self.check_next(TokenType::Not) || self.check_next(TokenType::In))
24864            {
24865                self.skip();
24866                global_in = true;
24867            }
24868
24869            let expr = if self.match_token(TokenType::Eq) {
24870                // Check for ANY/ALL subquery
24871                if self.match_token(TokenType::Any) || self.match_token(TokenType::Some) {
24872                    let was_any = self.previous_token_type() == Some(TokenType::Any);
24873                    self.expect(TokenType::LParen)?;
24874                    let inner = self.parse_statement()?;
24875                    self.expect(TokenType::RParen)?;
24876                    let subquery = if was_any {
24877                        self.maybe_wrap_in_subquery(inner)
24878                    } else {
24879                        inner
24880                    };
24881                    Expression::Any(Box::new(QuantifiedExpr {
24882                        this: left,
24883                        subquery,
24884                        op: Some(QuantifiedOp::Eq),
24885                    }))
24886                } else if self.match_token(TokenType::All) {
24887                    self.expect(TokenType::LParen)?;
24888                    let inner = self.parse_statement()?;
24889                    self.expect(TokenType::RParen)?;
24890                    let subquery = self.maybe_wrap_in_subquery(inner);
24891                    Expression::All(Box::new(QuantifiedExpr {
24892                        this: left,
24893                        subquery,
24894                        op: Some(QuantifiedOp::Eq),
24895                    }))
24896                } else {
24897                    let right = self.parse_bitwise_or()?;
24898                    let trailing_comments = self.previous_trailing_comments().to_vec();
24899                    Expression::Eq(Box::new(BinaryOp {
24900                        left,
24901                        right,
24902                        left_comments: Vec::new(),
24903                        operator_comments: Vec::new(),
24904                        trailing_comments,
24905                        inferred_type: None,
24906                    }))
24907                }
24908            } else if self.match_token(TokenType::Neq) {
24909                // Check for ANY/ALL subquery
24910                if self.match_token(TokenType::Any) || self.match_token(TokenType::Some) {
24911                    let was_any = self.previous_token_type() == Some(TokenType::Any);
24912                    self.expect(TokenType::LParen)?;
24913                    let inner = self.parse_statement()?;
24914                    self.expect(TokenType::RParen)?;
24915                    let subquery = if was_any {
24916                        self.maybe_wrap_in_subquery(inner)
24917                    } else {
24918                        inner
24919                    };
24920                    Expression::Any(Box::new(QuantifiedExpr {
24921                        this: left,
24922                        subquery,
24923                        op: Some(QuantifiedOp::Neq),
24924                    }))
24925                } else if self.match_token(TokenType::All) {
24926                    self.expect(TokenType::LParen)?;
24927                    let inner = self.parse_statement()?;
24928                    self.expect(TokenType::RParen)?;
24929                    let subquery = self.maybe_wrap_in_subquery(inner);
24930                    Expression::All(Box::new(QuantifiedExpr {
24931                        this: left,
24932                        subquery,
24933                        op: Some(QuantifiedOp::Neq),
24934                    }))
24935                } else {
24936                    let right = self.parse_bitwise_or()?;
24937                    let trailing_comments = self.previous_trailing_comments().to_vec();
24938                    Expression::Neq(Box::new(BinaryOp {
24939                        left,
24940                        right,
24941                        left_comments: Vec::new(),
24942                        operator_comments: Vec::new(),
24943                        trailing_comments,
24944                        inferred_type: None,
24945                    }))
24946                }
24947            } else if self.match_token(TokenType::Lt) {
24948                // Check for ANY/ALL subquery
24949                if self.match_token(TokenType::Any) || self.match_token(TokenType::Some) {
24950                    let was_any = self.previous_token_type() == Some(TokenType::Any);
24951                    self.expect(TokenType::LParen)?;
24952                    let inner = self.parse_statement()?;
24953                    self.expect(TokenType::RParen)?;
24954                    let subquery = if was_any {
24955                        self.maybe_wrap_in_subquery(inner)
24956                    } else {
24957                        inner
24958                    };
24959                    Expression::Any(Box::new(QuantifiedExpr {
24960                        this: left,
24961                        subquery,
24962                        op: Some(QuantifiedOp::Lt),
24963                    }))
24964                } else if self.match_token(TokenType::All) {
24965                    self.expect(TokenType::LParen)?;
24966                    let inner = self.parse_statement()?;
24967                    self.expect(TokenType::RParen)?;
24968                    let subquery = self.maybe_wrap_in_subquery(inner);
24969                    Expression::All(Box::new(QuantifiedExpr {
24970                        this: left,
24971                        subquery,
24972                        op: Some(QuantifiedOp::Lt),
24973                    }))
24974                } else {
24975                    let right = self.parse_bitwise_or()?;
24976                    let trailing_comments = self.previous_trailing_comments().to_vec();
24977                    Expression::Lt(Box::new(BinaryOp {
24978                        left,
24979                        right,
24980                        left_comments: Vec::new(),
24981                        operator_comments: Vec::new(),
24982                        trailing_comments,
24983                        inferred_type: None,
24984                    }))
24985                }
24986            } else if self.match_token(TokenType::Lte) {
24987                // Check for ANY/ALL subquery
24988                if self.match_token(TokenType::Any) || self.match_token(TokenType::Some) {
24989                    let was_any = self.previous_token_type() == Some(TokenType::Any);
24990                    self.expect(TokenType::LParen)?;
24991                    let inner = self.parse_statement()?;
24992                    self.expect(TokenType::RParen)?;
24993                    let subquery = if was_any {
24994                        self.maybe_wrap_in_subquery(inner)
24995                    } else {
24996                        inner
24997                    };
24998                    Expression::Any(Box::new(QuantifiedExpr {
24999                        this: left,
25000                        subquery,
25001                        op: Some(QuantifiedOp::Lte),
25002                    }))
25003                } else if self.match_token(TokenType::All) {
25004                    self.expect(TokenType::LParen)?;
25005                    let inner = self.parse_statement()?;
25006                    self.expect(TokenType::RParen)?;
25007                    let subquery = self.maybe_wrap_in_subquery(inner);
25008                    Expression::All(Box::new(QuantifiedExpr {
25009                        this: left,
25010                        subquery,
25011                        op: Some(QuantifiedOp::Lte),
25012                    }))
25013                } else {
25014                    let right = self.parse_bitwise_or()?;
25015                    let trailing_comments = self.previous_trailing_comments().to_vec();
25016                    Expression::Lte(Box::new(BinaryOp {
25017                        left,
25018                        right,
25019                        left_comments: Vec::new(),
25020                        operator_comments: Vec::new(),
25021                        trailing_comments,
25022                        inferred_type: None,
25023                    }))
25024                }
25025            } else if self.match_token(TokenType::Gt) {
25026                // Check for ANY/ALL subquery
25027                if self.match_token(TokenType::Any) || self.match_token(TokenType::Some) {
25028                    let was_any = self.previous_token_type() == Some(TokenType::Any);
25029                    self.expect(TokenType::LParen)?;
25030                    let inner = self.parse_statement()?;
25031                    self.expect(TokenType::RParen)?;
25032                    let subquery = if was_any {
25033                        self.maybe_wrap_in_subquery(inner)
25034                    } else {
25035                        inner
25036                    };
25037                    Expression::Any(Box::new(QuantifiedExpr {
25038                        this: left,
25039                        subquery,
25040                        op: Some(QuantifiedOp::Gt),
25041                    }))
25042                } else if self.match_token(TokenType::All) {
25043                    self.expect(TokenType::LParen)?;
25044                    let inner = self.parse_statement()?;
25045                    self.expect(TokenType::RParen)?;
25046                    let subquery = self.maybe_wrap_in_subquery(inner);
25047                    Expression::All(Box::new(QuantifiedExpr {
25048                        this: left,
25049                        subquery,
25050                        op: Some(QuantifiedOp::Gt),
25051                    }))
25052                } else {
25053                    let right = self.parse_bitwise_or()?;
25054                    let trailing_comments = self.previous_trailing_comments().to_vec();
25055                    Expression::Gt(Box::new(BinaryOp {
25056                        left,
25057                        right,
25058                        left_comments: Vec::new(),
25059                        operator_comments: Vec::new(),
25060                        trailing_comments,
25061                        inferred_type: None,
25062                    }))
25063                }
25064            } else if self.match_token(TokenType::Gte) {
25065                // Check for ANY/ALL subquery
25066                if self.match_token(TokenType::Any) || self.match_token(TokenType::Some) {
25067                    let was_any = self.previous_token_type() == Some(TokenType::Any);
25068                    self.expect(TokenType::LParen)?;
25069                    let inner = self.parse_statement()?;
25070                    self.expect(TokenType::RParen)?;
25071                    let subquery = if was_any {
25072                        self.maybe_wrap_in_subquery(inner)
25073                    } else {
25074                        inner
25075                    };
25076                    Expression::Any(Box::new(QuantifiedExpr {
25077                        this: left,
25078                        subquery,
25079                        op: Some(QuantifiedOp::Gte),
25080                    }))
25081                } else if self.match_token(TokenType::All) {
25082                    self.expect(TokenType::LParen)?;
25083                    let inner = self.parse_statement()?;
25084                    self.expect(TokenType::RParen)?;
25085                    let subquery = self.maybe_wrap_in_subquery(inner);
25086                    Expression::All(Box::new(QuantifiedExpr {
25087                        this: left,
25088                        subquery,
25089                        op: Some(QuantifiedOp::Gte),
25090                    }))
25091                } else {
25092                    let right = self.parse_bitwise_or()?;
25093                    let trailing_comments = self.previous_trailing_comments().to_vec();
25094                    Expression::Gte(Box::new(BinaryOp {
25095                        left,
25096                        right,
25097                        left_comments: Vec::new(),
25098                        operator_comments: Vec::new(),
25099                        trailing_comments,
25100                        inferred_type: None,
25101                    }))
25102                }
25103            } else if self.match_token(TokenType::NullsafeEq) {
25104                // <=> (MySQL NULL-safe equality)
25105                let right = self.parse_bitwise_or()?;
25106                let trailing_comments = self.previous_trailing_comments().to_vec();
25107                Expression::NullSafeEq(Box::new(BinaryOp {
25108                    left,
25109                    right,
25110                    left_comments: Vec::new(),
25111                    operator_comments: Vec::new(),
25112                    trailing_comments,
25113                    inferred_type: None,
25114                }))
25115            } else if self.check_identifier("SOUNDS") && self.check_next(TokenType::Like) {
25116                // MySQL SOUNDS LIKE: expr SOUNDS LIKE expr -> SOUNDEX(expr) = SOUNDEX(expr)
25117                self.skip(); // consume SOUNDS
25118                self.skip(); // consume LIKE
25119                let right = self.parse_bitwise_or()?;
25120                // Transform: SOUNDEX(left) = SOUNDEX(right)
25121                let soundex_left = Expression::Function(Box::new(Function::new(
25122                    "SOUNDEX".to_string(),
25123                    vec![left],
25124                )));
25125                let soundex_right = Expression::Function(Box::new(Function::new(
25126                    "SOUNDEX".to_string(),
25127                    vec![right],
25128                )));
25129                Expression::Eq(Box::new(BinaryOp::new(soundex_left, soundex_right)))
25130            } else if self.match_token(TokenType::Like) {
25131                // Check for ANY/ALL/SOME quantifier
25132                let quantifier = if self.match_token(TokenType::Any) {
25133                    Some("ANY".to_string())
25134                } else if self.match_token(TokenType::All) {
25135                    Some("ALL".to_string())
25136                } else if self.match_token(TokenType::Some) {
25137                    Some("SOME".to_string())
25138                } else {
25139                    None
25140                };
25141                let right = self.parse_bitwise_or()?;
25142                let escape = if self.match_token(TokenType::Escape) {
25143                    Some(self.parse_primary()?)
25144                } else {
25145                    None
25146                };
25147                Expression::Like(Box::new(LikeOp {
25148                    left,
25149                    right,
25150                    escape,
25151                    quantifier,
25152                    inferred_type: None,
25153                }))
25154            } else if self.match_token(TokenType::ILike) {
25155                // Check for ANY/ALL/SOME quantifier
25156                let quantifier = if self.match_token(TokenType::Any) {
25157                    Some("ANY".to_string())
25158                } else if self.match_token(TokenType::All) {
25159                    Some("ALL".to_string())
25160                } else if self.match_token(TokenType::Some) {
25161                    Some("SOME".to_string())
25162                } else {
25163                    None
25164                };
25165                let right = self.parse_bitwise_or()?;
25166                let escape = if self.match_token(TokenType::Escape) {
25167                    Some(self.parse_primary()?)
25168                } else {
25169                    None
25170                };
25171                Expression::ILike(Box::new(LikeOp {
25172                    left,
25173                    right,
25174                    escape,
25175                    quantifier,
25176                    inferred_type: None,
25177                }))
25178            } else if self.check_identifier("SIMILAR") && self.check_next(TokenType::To) {
25179                // SIMILAR TO operator (PostgreSQL/Redshift regex-like pattern matching)
25180                self.skip(); // consume SIMILAR
25181                self.skip(); // consume TO
25182                let pattern = self.parse_bitwise_or()?;
25183                let escape = if self.match_token(TokenType::Escape) {
25184                    Some(self.parse_primary()?)
25185                } else {
25186                    None
25187                };
25188                Expression::SimilarTo(Box::new(SimilarToExpr {
25189                    this: left,
25190                    pattern,
25191                    escape,
25192                    not: false,
25193                }))
25194            } else if self.match_token(TokenType::Glob) {
25195                let right = self.parse_bitwise_or()?;
25196                Expression::Glob(Box::new(BinaryOp::new(left, right)))
25197            } else if self.match_token(TokenType::Match) {
25198                // SQLite MATCH operator (FTS full-text search)
25199                let right = self.parse_bitwise_or()?;
25200                Expression::Match(Box::new(BinaryOp::new(left, right)))
25201            } else if self.match_token(TokenType::RLike) || self.match_token(TokenType::Tilde) {
25202                // PostgreSQL ~ (regexp match) operator
25203                let right = self.parse_bitwise_or()?;
25204                Expression::RegexpLike(Box::new(RegexpFunc {
25205                    this: left,
25206                    pattern: right,
25207                    flags: None,
25208                }))
25209            } else if self.match_token(TokenType::IRLike) {
25210                // PostgreSQL ~* (case-insensitive regexp match) operator
25211                let right = self.parse_bitwise_or()?;
25212                Expression::RegexpILike(Box::new(RegexpILike {
25213                    this: Box::new(left),
25214                    expression: Box::new(right),
25215                    flag: None,
25216                }))
25217            } else if self.match_token(TokenType::NotLike) {
25218                // PostgreSQL !~~ (NOT LIKE) operator
25219                let right = self.parse_bitwise_or()?;
25220                let escape = if self.match_token(TokenType::Escape) {
25221                    Some(self.parse_primary()?)
25222                } else {
25223                    None
25224                };
25225                let like_expr = Expression::Like(Box::new(LikeOp {
25226                    left,
25227                    right,
25228                    escape,
25229                    quantifier: None,
25230                    inferred_type: None,
25231                }));
25232                Expression::Not(Box::new(UnaryOp::new(like_expr)))
25233            } else if self.match_token(TokenType::NotILike) {
25234                // PostgreSQL !~~* (NOT ILIKE) operator
25235                let right = self.parse_bitwise_or()?;
25236                let escape = if self.match_token(TokenType::Escape) {
25237                    Some(self.parse_primary()?)
25238                } else {
25239                    None
25240                };
25241                let ilike_expr = Expression::ILike(Box::new(LikeOp {
25242                    left,
25243                    right,
25244                    escape,
25245                    quantifier: None,
25246                    inferred_type: None,
25247                }));
25248                Expression::Not(Box::new(UnaryOp::new(ilike_expr)))
25249            } else if self.match_token(TokenType::NotRLike) {
25250                // PostgreSQL !~ (NOT regexp match) operator
25251                let right = self.parse_bitwise_or()?;
25252                let regexp_expr = Expression::RegexpLike(Box::new(RegexpFunc {
25253                    this: left,
25254                    pattern: right,
25255                    flags: None,
25256                }));
25257                Expression::Not(Box::new(UnaryOp::new(regexp_expr)))
25258            } else if self.match_token(TokenType::NotIRLike) {
25259                // PostgreSQL !~* (NOT case-insensitive regexp match) operator
25260                let right = self.parse_bitwise_or()?;
25261                let regexp_expr = Expression::RegexpILike(Box::new(RegexpILike {
25262                    this: Box::new(left),
25263                    expression: Box::new(right),
25264                    flag: None,
25265                }));
25266                Expression::Not(Box::new(UnaryOp::new(regexp_expr)))
25267            } else if self.check(TokenType::Is)
25268                && !self.is_last_expression_token(TokenType::Is)
25269                && self.match_token(TokenType::Is)
25270            {
25271                let not = self.match_token(TokenType::Not);
25272                if self.match_token(TokenType::Null) {
25273                    let expr = Expression::IsNull(Box::new(IsNull {
25274                        this: left,
25275                        not,
25276                        postfix_form: false,
25277                    }));
25278                    // ClickHouse: IS NULL :: Type — handle :: cast after IS NULL
25279                    if matches!(
25280                        self.config.dialect,
25281                        Some(crate::dialects::DialectType::ClickHouse)
25282                    ) && self.check(TokenType::DColon)
25283                    {
25284                        self.skip(); // consume ::
25285                        let data_type = self.parse_data_type_for_cast()?;
25286                        Expression::Cast(Box::new(Cast {
25287                            this: expr,
25288                            to: data_type,
25289                            trailing_comments: Vec::new(),
25290                            double_colon_syntax: true,
25291                            format: None,
25292                            default: None,
25293                            inferred_type: None,
25294                        }))
25295                    } else {
25296                        expr
25297                    }
25298                } else if self.match_token(TokenType::True) {
25299                    // IS TRUE / IS NOT TRUE
25300                    Expression::IsTrue(Box::new(IsTrueFalse { this: left, not }))
25301                } else if self.match_token(TokenType::False) {
25302                    // IS FALSE / IS NOT FALSE
25303                    Expression::IsFalse(Box::new(IsTrueFalse { this: left, not }))
25304                } else if self.match_token(TokenType::Distinct) {
25305                    // IS DISTINCT FROM / IS NOT DISTINCT FROM
25306                    self.expect(TokenType::From)?;
25307                    let right = self.parse_bitwise_or()?;
25308                    if not {
25309                        // IS NOT DISTINCT FROM → null-safe equality
25310                        Expression::NullSafeEq(Box::new(BinaryOp::new(left, right)))
25311                    } else {
25312                        // IS DISTINCT FROM → null-safe inequality
25313                        Expression::NullSafeNeq(Box::new(BinaryOp::new(left, right)))
25314                    }
25315                } else if self.match_identifier("UNKNOWN") {
25316                    // IS UNKNOWN
25317                    Expression::IsNull(Box::new(IsNull {
25318                        this: left,
25319                        not,
25320                        postfix_form: false,
25321                    }))
25322                } else if self.match_texts(&["JSON"]) {
25323                    // IS JSON [VALUE|SCALAR|OBJECT|ARRAY] [WITH UNIQUE KEYS|WITHOUT UNIQUE KEYS|UNIQUE KEYS]
25324                    let json_type = if self.match_texts(&["VALUE"]) {
25325                        Some("VALUE".to_string())
25326                    } else if self.match_texts(&["SCALAR"]) {
25327                        Some("SCALAR".to_string())
25328                    } else if self.match_texts(&["OBJECT"]) {
25329                        Some("OBJECT".to_string())
25330                    } else if self.match_texts(&["ARRAY"]) {
25331                        Some("ARRAY".to_string())
25332                    } else {
25333                        None
25334                    };
25335
25336                    // Parse optional key uniqueness constraint
25337                    let unique_keys = if self.match_text_seq(&["WITH", "UNIQUE", "KEYS"]) {
25338                        Some(JsonUniqueKeys::With)
25339                    } else if self.match_text_seq(&["WITHOUT", "UNIQUE", "KEYS"]) {
25340                        Some(JsonUniqueKeys::Without)
25341                    } else if self.match_text_seq(&["UNIQUE", "KEYS"]) {
25342                        // Shorthand for WITH UNIQUE KEYS
25343                        Some(JsonUniqueKeys::Shorthand)
25344                    } else {
25345                        None
25346                    };
25347
25348                    Expression::IsJson(Box::new(IsJson {
25349                        this: left,
25350                        json_type,
25351                        unique_keys,
25352                        negated: not,
25353                    }))
25354                } else {
25355                    // IS followed by an expression (e.g., IS ?)
25356                    // If we matched NOT, wrap the IS expression in NOT
25357                    let right = self.parse_primary()?;
25358                    let is_expr = Expression::Is(Box::new(BinaryOp::new(left, right)));
25359                    if not {
25360                        Expression::Not(Box::new(UnaryOp::new(is_expr)))
25361                    } else {
25362                        is_expr
25363                    }
25364                }
25365            } else if self.match_token(TokenType::Not) {
25366                // Handle NOT IN, NOT BETWEEN, NOT LIKE, NOT ILIKE, etc.
25367                if self.match_token(TokenType::In) {
25368                    // BigQuery: NOT IN UNNEST(expr)
25369                    if self.check_identifier("UNNEST") {
25370                        self.skip(); // consume UNNEST
25371                        self.expect(TokenType::LParen)?;
25372                        let unnest_expr = self.parse_expression()?;
25373                        self.expect(TokenType::RParen)?;
25374                        Expression::In(Box::new(In {
25375                            this: left,
25376                            expressions: Vec::new(),
25377                            query: None,
25378                            not: true,
25379                            global: global_in,
25380                            unnest: Some(Box::new(unnest_expr)),
25381                            is_field: false,
25382                        }))
25383                    } else if self.match_token(TokenType::LParen) {
25384                        if self.check(TokenType::Select) || self.check(TokenType::With) {
25385                            let subquery = self.parse_statement()?;
25386                            self.expect(TokenType::RParen)?;
25387                            Expression::In(Box::new(In {
25388                                this: left,
25389                                expressions: Vec::new(),
25390                                query: Some(subquery),
25391                                not: true,
25392                                global: global_in,
25393                                unnest: None,
25394                                is_field: false,
25395                            }))
25396                        } else if self.check(TokenType::RParen) {
25397                            // Empty NOT IN set: NOT IN ()
25398                            self.skip();
25399                            Expression::In(Box::new(In {
25400                                this: left,
25401                                expressions: Vec::new(),
25402                                query: None,
25403                                not: true,
25404                                global: global_in,
25405                                unnest: None,
25406                                is_field: false,
25407                            }))
25408                        } else {
25409                            let expressions = self.parse_expression_list()?;
25410                            self.expect(TokenType::RParen)?;
25411                            Expression::In(Box::new(In {
25412                                this: left,
25413                                expressions,
25414                                query: None,
25415                                not: true,
25416                                global: global_in,
25417                                unnest: None,
25418                                is_field: false,
25419                            }))
25420                        }
25421                    } else {
25422                        // ClickHouse/DuckDB: IN without parentheses: expr NOT IN table_name
25423                        let table_expr = self.parse_primary()?;
25424                        Expression::In(Box::new(In {
25425                            this: left,
25426                            expressions: vec![table_expr],
25427                            query: None,
25428                            not: true,
25429                            global: global_in,
25430                            unnest: None,
25431                            is_field: true,
25432                        }))
25433                    }
25434                } else if self.match_token(TokenType::Between) {
25435                    // Check for SYMMETRIC/ASYMMETRIC qualifier
25436                    let symmetric = if self.match_texts(&["SYMMETRIC"]) {
25437                        Some(true)
25438                    } else if self.match_texts(&["ASYMMETRIC"]) {
25439                        Some(false)
25440                    } else {
25441                        None
25442                    };
25443                    let low = self.parse_bitwise_or()?;
25444                    self.expect(TokenType::And)?;
25445                    let high = self.parse_bitwise_or()?;
25446                    Expression::Between(Box::new(Between {
25447                        this: left,
25448                        low,
25449                        high,
25450                        not: true,
25451                        symmetric,
25452                    }))
25453                } else if self.check_identifier("SOUNDS") && self.check_next(TokenType::Like) {
25454                    // MySQL NOT SOUNDS LIKE: expr NOT SOUNDS LIKE expr -> NOT SOUNDEX(expr) = SOUNDEX(expr)
25455                    self.skip(); // consume SOUNDS
25456                    self.skip(); // consume LIKE
25457                    let right = self.parse_bitwise_or()?;
25458                    let soundex_left = Expression::Function(Box::new(Function::new(
25459                        "SOUNDEX".to_string(),
25460                        vec![left],
25461                    )));
25462                    let soundex_right = Expression::Function(Box::new(Function::new(
25463                        "SOUNDEX".to_string(),
25464                        vec![right],
25465                    )));
25466                    let eq_expr =
25467                        Expression::Eq(Box::new(BinaryOp::new(soundex_left, soundex_right)));
25468                    Expression::Not(Box::new(UnaryOp::new(eq_expr)))
25469                } else if self.match_token(TokenType::Like) {
25470                    let right = self.parse_bitwise_or()?;
25471                    let escape = if self.match_token(TokenType::Escape) {
25472                        Some(self.parse_primary()?)
25473                    } else {
25474                        None
25475                    };
25476                    let like_expr = Expression::Like(Box::new(LikeOp {
25477                        left,
25478                        right,
25479                        escape,
25480                        quantifier: None,
25481                        inferred_type: None,
25482                    }));
25483                    Expression::Not(Box::new(UnaryOp::new(like_expr)))
25484                } else if self.match_token(TokenType::ILike) {
25485                    let right = self.parse_bitwise_or()?;
25486                    let escape = if self.match_token(TokenType::Escape) {
25487                        Some(self.parse_primary()?)
25488                    } else {
25489                        None
25490                    };
25491                    let ilike_expr = Expression::ILike(Box::new(LikeOp {
25492                        left,
25493                        right,
25494                        escape,
25495                        quantifier: None,
25496                        inferred_type: None,
25497                    }));
25498                    Expression::Not(Box::new(UnaryOp::new(ilike_expr)))
25499                } else if self.check_identifier("SIMILAR") && self.check_next(TokenType::To) {
25500                    // NOT SIMILAR TO
25501                    self.skip(); // consume SIMILAR
25502                    self.skip(); // consume TO
25503                    let pattern = self.parse_bitwise_or()?;
25504                    let escape = if self.match_token(TokenType::Escape) {
25505                        Some(self.parse_primary()?)
25506                    } else {
25507                        None
25508                    };
25509                    Expression::SimilarTo(Box::new(SimilarToExpr {
25510                        this: left,
25511                        pattern,
25512                        escape,
25513                        not: true,
25514                    }))
25515                } else if self.match_token(TokenType::RLike) {
25516                    let right = self.parse_bitwise_or()?;
25517                    let regexp_expr = Expression::RegexpLike(Box::new(RegexpFunc {
25518                        this: left,
25519                        pattern: right,
25520                        flags: None,
25521                    }));
25522                    Expression::Not(Box::new(UnaryOp::new(regexp_expr)))
25523                } else if self.match_token(TokenType::Null) {
25524                    // SQLite: a NOT NULL (postfix form, two separate tokens)
25525                    // Creates NOT(a IS NULL) which is semantically equivalent
25526                    let is_null =
25527                        Expression::Is(Box::new(BinaryOp::new(left, Expression::Null(Null))));
25528                    Expression::Not(Box::new(UnaryOp::new(is_null)))
25529                } else {
25530                    // NOT followed by something else - revert
25531                    return Ok(left);
25532                }
25533            } else if self.match_token(TokenType::In) {
25534                // BigQuery: IN UNNEST(expr)
25535                if self.check_identifier("UNNEST") {
25536                    self.skip(); // consume UNNEST
25537                    self.expect(TokenType::LParen)?;
25538                    let unnest_expr = self.parse_expression()?;
25539                    self.expect(TokenType::RParen)?;
25540                    Expression::In(Box::new(In {
25541                        this: left,
25542                        expressions: Vec::new(),
25543                        query: None,
25544                        not: false,
25545                        global: global_in,
25546                        unnest: Some(Box::new(unnest_expr)),
25547                        is_field: false,
25548                    }))
25549                } else if self.match_token(TokenType::LParen) {
25550                    // Standard IN (list) or IN (subquery)
25551                    // Check if this is a subquery (IN (SELECT ...) or IN (WITH ... SELECT ...))
25552                    if self.check(TokenType::Select) || self.check(TokenType::With) {
25553                        // Use parse_statement to handle both SELECT and WITH...SELECT
25554                        let subquery = self.parse_statement()?;
25555                        self.expect(TokenType::RParen)?;
25556                        Expression::In(Box::new(In {
25557                            this: left,
25558                            expressions: Vec::new(),
25559                            query: Some(subquery),
25560                            not: false,
25561                            global: global_in,
25562                            unnest: None,
25563                            is_field: false,
25564                        }))
25565                    } else if self.check(TokenType::RParen) {
25566                        // Empty IN set: IN ()
25567                        self.skip();
25568                        Expression::In(Box::new(In {
25569                            this: left,
25570                            expressions: Vec::new(),
25571                            query: None,
25572                            not: false,
25573                            global: global_in,
25574                            unnest: None,
25575                            is_field: false,
25576                        }))
25577                    } else {
25578                        let expressions = self.parse_expression_list()?;
25579                        self.expect(TokenType::RParen)?;
25580                        Expression::In(Box::new(In {
25581                            this: left,
25582                            expressions,
25583                            query: None,
25584                            not: false,
25585                            global: global_in,
25586                            unnest: None,
25587                            is_field: false,
25588                        }))
25589                    }
25590                } else {
25591                    // DuckDB: IN without parentheses for array/list membership: 'red' IN tbl.flags
25592                    let expr = self.parse_bitwise_or()?;
25593                    Expression::In(Box::new(In {
25594                        this: left,
25595                        expressions: vec![expr],
25596                        query: None,
25597                        not: false,
25598                        global: global_in,
25599                        unnest: None,
25600                        is_field: true,
25601                    }))
25602                }
25603            } else if self.match_token(TokenType::Between) {
25604                // Check for SYMMETRIC/ASYMMETRIC qualifier
25605                let symmetric = if self.match_texts(&["SYMMETRIC"]) {
25606                    Some(true)
25607                } else if self.match_texts(&["ASYMMETRIC"]) {
25608                    Some(false)
25609                } else {
25610                    None
25611                };
25612                let low = self.parse_bitwise_or()?;
25613                self.expect(TokenType::And)?;
25614                let high = self.parse_bitwise_or()?;
25615                Expression::Between(Box::new(Between {
25616                    this: left,
25617                    low,
25618                    high,
25619                    not: false,
25620                    symmetric,
25621                }))
25622            } else if self.match_token(TokenType::Adjacent) {
25623                let right = self.parse_bitwise_or()?;
25624                Expression::Adjacent(Box::new(BinaryOp::new(left, right)))
25625            } else if self.check(TokenType::Overlaps)
25626                && self.current + 1 < self.tokens.len()
25627                && !matches!(
25628                    self.tokens[self.current + 1].token_type,
25629                    TokenType::Semicolon
25630                        | TokenType::Comma
25631                        | TokenType::From
25632                        | TokenType::Where
25633                        | TokenType::RParen
25634                        | TokenType::As
25635                        | TokenType::Join
25636                        | TokenType::On
25637                        | TokenType::OrderBy
25638                        | TokenType::GroupBy
25639                        | TokenType::Having
25640                        | TokenType::Limit
25641                        | TokenType::Union
25642                        | TokenType::Except
25643                        | TokenType::Intersect
25644                        | TokenType::Eof
25645                )
25646            {
25647                self.skip(); // consume OVERLAPS
25648                let right = self.parse_bitwise_or()?;
25649                Expression::Overlaps(Box::new(OverlapsExpr {
25650                    this: Some(left),
25651                    expression: Some(right),
25652                    left_start: None,
25653                    left_end: None,
25654                    right_start: None,
25655                    right_end: None,
25656                }))
25657            } else if self.match_token(TokenType::IsNull) {
25658                // ISNULL postfix operator (PostgreSQL/SQLite)
25659                Expression::IsNull(Box::new(IsNull {
25660                    this: left,
25661                    not: false,
25662                    postfix_form: true,
25663                }))
25664            } else if self.match_token(TokenType::NotNull) {
25665                // NOTNULL postfix operator (PostgreSQL/SQLite)
25666                Expression::IsNull(Box::new(IsNull {
25667                    this: left,
25668                    not: true,
25669                    postfix_form: true,
25670                }))
25671            } else if self.match_token(TokenType::AtAt) {
25672                // PostgreSQL text search match operator (@@)
25673                let right = self.parse_bitwise_or()?;
25674                Expression::TsMatch(Box::new(BinaryOp::new(left, right)))
25675            } else if self.match_token(TokenType::AtGt) {
25676                // PostgreSQL array contains all operator (@>)
25677                let right = self.parse_bitwise_or()?;
25678                Expression::ArrayContainsAll(Box::new(BinaryOp::new(left, right)))
25679            } else if self.match_token(TokenType::LtAt) {
25680                // PostgreSQL array contained by operator (<@)
25681                let right = self.parse_bitwise_or()?;
25682                Expression::ArrayContainedBy(Box::new(BinaryOp::new(left, right)))
25683            } else if self.match_token(TokenType::DAmp) {
25684                // PostgreSQL array overlaps operator (&&)
25685                let right = self.parse_bitwise_or()?;
25686                Expression::ArrayOverlaps(Box::new(BinaryOp::new(left, right)))
25687            } else if self.match_token(TokenType::QMarkAmp) {
25688                // PostgreSQL JSONB contains all top keys operator (?&)
25689                let right = self.parse_bitwise_or()?;
25690                Expression::JSONBContainsAllTopKeys(Box::new(BinaryOp::new(left, right)))
25691            } else if self.match_token(TokenType::QMarkPipe) {
25692                // PostgreSQL JSONB contains any top key operator (?|)
25693                let right = self.parse_bitwise_or()?;
25694                Expression::JSONBContainsAnyTopKeys(Box::new(BinaryOp::new(left, right)))
25695            } else if !matches!(
25696                self.config.dialect,
25697                Some(crate::dialects::DialectType::ClickHouse)
25698            ) && self.match_token(TokenType::Parameter)
25699            {
25700                // PostgreSQL JSONB contains key operator (?)
25701                // Note: ? is tokenized as Parameter, but when used between expressions
25702                // it's the JSONB key existence operator
25703                // ClickHouse uses ? as ternary operator instead, handled in parse_assignment()
25704                let right = self.parse_bitwise_or()?;
25705                Expression::JSONBContains(Box::new(BinaryFunc {
25706                    original_name: Some("?".to_string()),
25707                    this: left,
25708                    expression: right,
25709                    inferred_type: None,
25710                }))
25711            } else if self.match_token(TokenType::HashDash) {
25712                // PostgreSQL JSONB delete at path operator (#-)
25713                let right = self.parse_bitwise_or()?;
25714                Expression::JSONBDeleteAtPath(Box::new(BinaryOp::new(left, right)))
25715            } else if self.match_token(TokenType::AmpLt) {
25716                // PostgreSQL range extends left operator (&<)
25717                let right = self.parse_bitwise_or()?;
25718                Expression::ExtendsLeft(Box::new(BinaryOp::new(left, right)))
25719            } else if self.match_token(TokenType::AmpGt) {
25720                // PostgreSQL range extends right operator (&>)
25721                let right = self.parse_bitwise_or()?;
25722                Expression::ExtendsRight(Box::new(BinaryOp::new(left, right)))
25723            } else if self.match_identifier("MEMBER") {
25724                // MySQL MEMBER OF(expr) operator - JSON membership test
25725                self.expect(TokenType::Of)?;
25726                self.expect(TokenType::LParen)?;
25727                let right = self.parse_expression()?;
25728                self.expect(TokenType::RParen)?;
25729                Expression::MemberOf(Box::new(BinaryOp::new(left, right)))
25730            } else if self.match_token(TokenType::CaretAt) {
25731                // DuckDB/PostgreSQL starts-with operator (^@)
25732                let right = self.parse_bitwise_or()?;
25733                Expression::StartsWith(Box::new(BinaryFunc {
25734                    original_name: Some("^@".to_string()),
25735                    this: left,
25736                    expression: right,
25737                    inferred_type: None,
25738                }))
25739            } else if self.match_token(TokenType::LrArrow) {
25740                // PostgreSQL distance operator (<->)
25741                let right = self.parse_bitwise_or()?;
25742                Expression::EuclideanDistance(Box::new(EuclideanDistance {
25743                    this: Box::new(left),
25744                    expression: Box::new(right),
25745                }))
25746            } else if self.match_token(TokenType::Operator) {
25747                // PostgreSQL OPERATOR(schema.op) syntax for schema-qualified operators
25748                // Example: col1 OPERATOR(pg_catalog.~) col2
25749                self.expect(TokenType::LParen)?;
25750
25751                // Collect all tokens between parentheses as the operator text
25752                // This can include schema names, dots, and operator symbols like ~
25753                let mut op_text = String::new();
25754                while !self.check(TokenType::RParen) && !self.is_at_end() {
25755                    op_text.push_str(&self.peek().text);
25756                    self.skip();
25757                }
25758                self.expect(TokenType::RParen)?;
25759
25760                // Collect any inline comments (e.g., /* foo */) between OPERATOR() and the RHS
25761                // Try trailing comments of the RParen (previous token) first,
25762                // then leading comments of the next token
25763                let mut comments = if self.current > 0 {
25764                    std::mem::take(&mut self.tokens[self.current - 1].trailing_comments)
25765                } else {
25766                    Vec::new()
25767                };
25768                if comments.is_empty() && !self.is_at_end() {
25769                    comments = std::mem::take(&mut self.tokens[self.current].comments);
25770                }
25771
25772                // Parse the right-hand side expression
25773                let right = self.parse_bitwise_or()?;
25774
25775                Expression::Operator(Box::new(Operator {
25776                    this: Box::new(left),
25777                    operator: Some(Box::new(Expression::Identifier(Identifier::new(op_text)))),
25778                    expression: Box::new(right),
25779                    comments,
25780                }))
25781            } else {
25782                return Ok(left);
25783            };
25784
25785            left = expr;
25786        }
25787    }
25788
25789    /// Parse bitwise OR expressions (|)
25790    fn parse_bitwise_or(&mut self) -> Result<Expression> {
25791        let mut left = self.parse_bitwise_xor()?;
25792
25793        loop {
25794            if self.match_token(TokenType::Pipe) {
25795                let right = self.parse_bitwise_xor()?;
25796                left = Expression::BitwiseOr(Box::new(BinaryOp::new(left, right)));
25797            } else {
25798                return Ok(left);
25799            }
25800        }
25801    }
25802
25803    /// Parse bitwise operators with an existing left expression
25804    /// Used for DuckDB's @ operator when @col is tokenized as a single Var token
25805    /// We already have the column, now need to continue parsing any binary operators
25806    /// Follows the same precedence chain: bitwise -> shift -> addition -> multiplication
25807    fn parse_bitwise_continuation(&mut self, left: Expression) -> Result<Expression> {
25808        // Start from multiplication level since we have a primary expression (col)
25809        // Then work up through addition, shift, bitwise AND/XOR/OR
25810        let mult_result = self.parse_multiplication_continuation(left)?;
25811        let add_result = self.parse_addition_continuation(mult_result)?;
25812        self.parse_bitwise_or_continuation(add_result)
25813    }
25814
25815    /// Parse bitwise OR with an existing left expression
25816    fn parse_bitwise_or_continuation(&mut self, mut left: Expression) -> Result<Expression> {
25817        loop {
25818            if self.match_token(TokenType::Pipe) {
25819                let right = self.parse_bitwise_xor()?;
25820                left = Expression::BitwiseOr(Box::new(BinaryOp::new(left, right)));
25821            } else {
25822                return Ok(left);
25823            }
25824        }
25825    }
25826
25827    /// Parse multiplication/division with an existing left expression
25828    fn parse_multiplication_continuation(&mut self, mut left: Expression) -> Result<Expression> {
25829        loop {
25830            let expr = if self.match_token(TokenType::Star) {
25831                let right = self.parse_power()?;
25832                Expression::Mul(Box::new(BinaryOp::new(left, right)))
25833            } else if self.match_token(TokenType::Slash) {
25834                let right = self.parse_power()?;
25835                Expression::Div(Box::new(BinaryOp::new(left, right)))
25836            } else if self.match_token(TokenType::Percent) {
25837                let right = self.parse_power()?;
25838                Expression::Mod(Box::new(BinaryOp::new(left, right)))
25839            } else if !self.check(TokenType::QuotedIdentifier)
25840                && (self.match_identifier("DIV") || self.match_token(TokenType::Div))
25841            {
25842                // DIV keyword for integer division (Hive/Spark/MySQL/ClickHouse)
25843                // Don't match QuotedIdentifier — `DIV` is an identifier alias, not an operator
25844                // If DIV was matched as a Var (not keyword Div token), verify it's actually
25845                // an operator by checking that a right operand follows. Otherwise it's an alias.
25846                let matched_as_var = self.previous().token_type == TokenType::Var;
25847                if matched_as_var
25848                    && (self.is_at_end()
25849                        || self.check(TokenType::Semicolon)
25850                        || self.check(TokenType::From)
25851                        || self.check(TokenType::Where)
25852                        || self.check(TokenType::Comma)
25853                        || self.check(TokenType::RParen))
25854                {
25855                    // Backtrack: DIV is being used as an alias, not an operator
25856                    self.current -= 1;
25857                    return Ok(left);
25858                }
25859                let right = self.parse_power()?;
25860                Expression::IntDiv(Box::new(crate::expressions::BinaryFunc {
25861                    this: left,
25862                    expression: right,
25863                    original_name: None,
25864                    inferred_type: None,
25865                }))
25866            } else {
25867                return Ok(left);
25868            };
25869            left = expr;
25870        }
25871    }
25872
25873    /// Parse addition/subtraction with an existing left expression
25874    fn parse_addition_continuation(&mut self, mut left: Expression) -> Result<Expression> {
25875        loop {
25876            let left_comments = self.previous_trailing_comments().to_vec();
25877
25878            let expr = if self.match_token(TokenType::Plus) {
25879                let operator_comments = self.previous_trailing_comments().to_vec();
25880                let right = self.parse_at_time_zone()?;
25881                let trailing_comments = self.previous_trailing_comments().to_vec();
25882                Expression::Add(Box::new(BinaryOp {
25883                    left,
25884                    right,
25885                    left_comments,
25886                    operator_comments,
25887                    trailing_comments,
25888                    inferred_type: None,
25889                }))
25890            } else if self.match_token(TokenType::Dash) {
25891                let operator_comments = self.previous_trailing_comments().to_vec();
25892                let right = self.parse_at_time_zone()?;
25893                let trailing_comments = self.previous_trailing_comments().to_vec();
25894                Expression::Sub(Box::new(BinaryOp {
25895                    left,
25896                    right,
25897                    left_comments,
25898                    operator_comments,
25899                    trailing_comments,
25900                    inferred_type: None,
25901                }))
25902            } else if !self.dpipe_is_logical_or() && self.match_token(TokenType::DPipe) {
25903                let operator_comments = self.previous_trailing_comments().to_vec();
25904                let right = self.parse_at_time_zone()?;
25905                let trailing_comments = self.previous_trailing_comments().to_vec();
25906                Expression::Concat(Box::new(BinaryOp {
25907                    left,
25908                    right,
25909                    left_comments,
25910                    operator_comments,
25911                    trailing_comments,
25912                    inferred_type: None,
25913                }))
25914            } else if self.match_token(TokenType::DQMark) {
25915                let right = self.parse_at_time_zone()?;
25916                Expression::Coalesce(Box::new(crate::expressions::VarArgFunc {
25917                    expressions: vec![left, right],
25918                    original_name: None,
25919                    inferred_type: None,
25920                }))
25921            } else {
25922                return Ok(left);
25923            };
25924
25925            left = expr;
25926        }
25927    }
25928
25929    /// Parse bitwise XOR expressions (^)
25930    fn parse_bitwise_xor(&mut self) -> Result<Expression> {
25931        let mut left = self.parse_bitwise_and()?;
25932
25933        loop {
25934            // In PostgreSQL, ^ is POWER (handled at parse_power level), and # is BitwiseXor
25935            if matches!(
25936                self.config.dialect,
25937                Some(crate::dialects::DialectType::PostgreSQL)
25938                    | Some(crate::dialects::DialectType::Redshift)
25939            ) {
25940                if self.match_token(TokenType::Hash) {
25941                    let right = self.parse_bitwise_and()?;
25942                    left = Expression::BitwiseXor(Box::new(BinaryOp::new(left, right)));
25943                } else {
25944                    return Ok(left);
25945                }
25946            } else if self.match_token(TokenType::Caret) {
25947                let right = self.parse_bitwise_and()?;
25948                left = Expression::BitwiseXor(Box::new(BinaryOp::new(left, right)));
25949            } else {
25950                return Ok(left);
25951            }
25952        }
25953    }
25954
25955    /// Parse bitwise AND expressions (&)
25956    fn parse_bitwise_and(&mut self) -> Result<Expression> {
25957        let mut left = self.parse_shift()?;
25958
25959        loop {
25960            if self.match_token(TokenType::Amp) {
25961                let right = self.parse_shift()?;
25962                left = Expression::BitwiseAnd(Box::new(BinaryOp::new(left, right)));
25963            } else {
25964                return Ok(left);
25965            }
25966        }
25967    }
25968
25969    /// Parse shift expressions (<< and >>)
25970    fn parse_shift(&mut self) -> Result<Expression> {
25971        let mut left = self.parse_addition()?;
25972
25973        loop {
25974            if self.match_token(TokenType::LtLt) {
25975                let right = self.parse_addition()?;
25976                left = Expression::BitwiseLeftShift(Box::new(BinaryOp::new(left, right)));
25977            } else if self.match_token(TokenType::GtGt) {
25978                let right = self.parse_addition()?;
25979                left = Expression::BitwiseRightShift(Box::new(BinaryOp::new(left, right)));
25980            } else {
25981                return Ok(left);
25982            }
25983        }
25984    }
25985
25986    /// Parse addition/subtraction
25987    fn parse_addition(&mut self) -> Result<Expression> {
25988        let mut left = self.parse_at_time_zone()?;
25989
25990        loop {
25991            // Capture comments after left operand before consuming operator
25992            let left_comments = self.previous_trailing_comments().to_vec();
25993
25994            let expr = if self.match_token(TokenType::Plus) {
25995                // Capture comments after operator (before right operand)
25996                let operator_comments = self.previous_trailing_comments().to_vec();
25997                let right = self.parse_at_time_zone()?;
25998                let trailing_comments = self.previous_trailing_comments().to_vec();
25999                Expression::Add(Box::new(BinaryOp {
26000                    left,
26001                    right,
26002                    left_comments,
26003                    operator_comments,
26004                    trailing_comments,
26005                    inferred_type: None,
26006                }))
26007            } else if self.match_token(TokenType::Dash) {
26008                let operator_comments = self.previous_trailing_comments().to_vec();
26009                let right = self.parse_at_time_zone()?;
26010                let trailing_comments = self.previous_trailing_comments().to_vec();
26011                Expression::Sub(Box::new(BinaryOp {
26012                    left,
26013                    right,
26014                    left_comments,
26015                    operator_comments,
26016                    trailing_comments,
26017                    inferred_type: None,
26018                }))
26019            } else if !self.dpipe_is_logical_or() && self.match_token(TokenType::DPipe) {
26020                let operator_comments = self.previous_trailing_comments().to_vec();
26021                let right = self.parse_at_time_zone()?;
26022                let trailing_comments = self.previous_trailing_comments().to_vec();
26023                Expression::Concat(Box::new(BinaryOp {
26024                    left,
26025                    right,
26026                    left_comments,
26027                    operator_comments,
26028                    trailing_comments,
26029                    inferred_type: None,
26030                }))
26031            } else if self.match_token(TokenType::DQMark) {
26032                let right = self.parse_at_time_zone()?;
26033                Expression::Coalesce(Box::new(crate::expressions::VarArgFunc {
26034                    expressions: vec![left, right],
26035                    original_name: None,
26036                    inferred_type: None,
26037                }))
26038            } else {
26039                return Ok(left);
26040            };
26041
26042            left = expr;
26043        }
26044    }
26045
26046    /// Parse AT TIME ZONE expression
26047    fn parse_at_time_zone(&mut self) -> Result<Expression> {
26048        let mut expr = self.parse_multiplication()?;
26049
26050        // Check for AT TIME ZONE (can be chained)
26051        while self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("AT") {
26052            self.skip(); // consume AT
26053                            // Check for TIME ZONE
26054            if self.check(TokenType::Time) {
26055                self.skip(); // consume TIME
26056                if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("ZONE") {
26057                    self.skip(); // consume ZONE
26058                    let zone = self.parse_unary()?;
26059                    expr = Expression::AtTimeZone(Box::new(AtTimeZone { this: expr, zone }));
26060                } else {
26061                    return Err(self.parse_error("Expected ZONE after AT TIME"));
26062                }
26063            } else {
26064                return Err(self.parse_error("Expected TIME after AT"));
26065            }
26066        }
26067
26068        Ok(expr)
26069    }
26070
26071    /// Parse multiplication/division
26072    fn parse_multiplication(&mut self) -> Result<Expression> {
26073        let mut left = self.parse_power()?;
26074
26075        loop {
26076            let expr = if self.match_token(TokenType::Star) {
26077                let right = self.parse_power()?;
26078                Expression::Mul(Box::new(BinaryOp::new(left, right)))
26079            } else if self.match_token(TokenType::Slash) {
26080                let right = self.parse_power()?;
26081                Expression::Div(Box::new(BinaryOp::new(left, right)))
26082            } else if self.match_token(TokenType::Percent) {
26083                let right = self.parse_power()?;
26084                Expression::Mod(Box::new(BinaryOp::new(left, right)))
26085            } else if !self.check(TokenType::QuotedIdentifier)
26086                && (self.match_identifier("MOD") || self.match_token(TokenType::Mod))
26087            {
26088                // MySQL/Teradata: x MOD y (infix modulo operator)
26089                // Don't match QuotedIdentifier — `MOD` is an identifier alias, not an operator
26090                let right = self.parse_power()?;
26091                Expression::Mod(Box::new(BinaryOp::new(left, right)))
26092            } else if !self.check(TokenType::QuotedIdentifier)
26093                && (self.match_identifier("DIV") || self.match_token(TokenType::Div))
26094            {
26095                // DIV keyword for integer division (Hive/Spark/MySQL/ClickHouse)
26096                // Don't match QuotedIdentifier — `DIV` is an identifier alias, not an operator
26097                // If DIV was matched as a Var (not keyword Div token), verify it's actually
26098                // an operator by checking that a right operand follows. Otherwise it's an alias.
26099                let matched_as_var = self.previous().token_type == TokenType::Var;
26100                if matched_as_var
26101                    && (self.is_at_end()
26102                        || self.check(TokenType::Semicolon)
26103                        || self.check(TokenType::From)
26104                        || self.check(TokenType::Where)
26105                        || self.check(TokenType::Comma)
26106                        || self.check(TokenType::RParen))
26107                {
26108                    // Backtrack: DIV is being used as an alias, not an operator
26109                    self.current -= 1;
26110                    return Ok(left);
26111                }
26112                let right = self.parse_power()?;
26113                Expression::IntDiv(Box::new(crate::expressions::BinaryFunc {
26114                    this: left,
26115                    expression: right,
26116                    original_name: None,
26117                    inferred_type: None,
26118                }))
26119            } else {
26120                return Ok(left);
26121            };
26122
26123            left = expr;
26124        }
26125    }
26126
26127    /// Parse power/exponentiation (**) operator
26128    /// In PostgreSQL/Redshift, ^ (Caret) is POWER, not BitwiseXor
26129    fn parse_power(&mut self) -> Result<Expression> {
26130        let mut left = self.parse_unary()?;
26131
26132        loop {
26133            if self.match_token(TokenType::DStar) {
26134                let right = self.parse_unary()?;
26135                left = Expression::Power(Box::new(BinaryFunc {
26136                    original_name: Some("**".to_string()),
26137                    this: left,
26138                    expression: right,
26139                    inferred_type: None,
26140                }));
26141            } else if matches!(
26142                self.config.dialect,
26143                Some(crate::dialects::DialectType::PostgreSQL)
26144                    | Some(crate::dialects::DialectType::Redshift)
26145                    | Some(crate::dialects::DialectType::DuckDB)
26146            ) && self.match_token(TokenType::Caret)
26147            {
26148                let right = self.parse_unary()?;
26149                left = Expression::Power(Box::new(BinaryFunc {
26150                    original_name: None,
26151                    this: left,
26152                    expression: right,
26153                    inferred_type: None,
26154                }));
26155            } else {
26156                return Ok(left);
26157            }
26158        }
26159    }
26160
26161    /// Try to parse a type literal expression like: point '(4,4)', timestamp '2024-01-01'
26162    /// PostgreSQL allows type name followed by string literal as a cast shorthand.
26163    /// Returns None if not a type literal pattern, so caller can fall through to parse_primary.
26164    fn try_parse_type_literal(&mut self) -> Result<Option<Expression>> {
26165        // Save position for backtracking
26166        let start_pos = self.current;
26167
26168        // Check if we're at an identifier or Var token that could be a type name
26169        if !self.check(TokenType::Identifier) && !self.check(TokenType::Var) {
26170            return Ok(None);
26171        }
26172
26173        // Get the potential type name without consuming
26174        let type_name = self.peek().text.to_ascii_uppercase();
26175
26176        // Check if this looks like a known data type that supports literal syntax
26177        // These are types where PostgreSQL allows TYPE 'value' syntax
26178        // NOTE: DATE, TIME, TIMESTAMP, INTERVAL are NOT here because they have their own
26179        // token types and are handled specially in parse_primary
26180        let is_type_literal_type = matches!(
26181            type_name.as_str(),
26182            // Geometric types (PostgreSQL)
26183            "POINT" | "LINE" | "LSEG" | "BOX" | "PATH" | "POLYGON" | "CIRCLE" |
26184            // Network types (PostgreSQL)
26185            "INET" | "CIDR" | "MACADDR" | "MACADDR8" |
26186            // Other types that support literal syntax
26187            "UUID" | "JSON" | "JSONB" | "XML" | "BIT" | "VARBIT" |
26188            // Range types (PostgreSQL)
26189            "INT4RANGE" | "INT8RANGE" | "NUMRANGE" | "TSRANGE" | "TSTZRANGE" | "DATERANGE"
26190        );
26191
26192        if !is_type_literal_type {
26193            return Ok(None);
26194        }
26195
26196        // Check if the next token (after type name) is a string literal
26197        if self.current + 1 >= self.tokens.len() {
26198            return Ok(None);
26199        }
26200
26201        if self.tokens[self.current + 1].token_type != TokenType::String {
26202            return Ok(None);
26203        }
26204
26205        // This looks like a type literal! Parse it.
26206        // Consume the type name
26207        self.skip();
26208
26209        // Try to parse the data type from the name
26210        let data_type = match self.parse_data_type_from_name(&type_name) {
26211            Ok(dt) => dt,
26212            Err(_) => {
26213                // If we can't parse the type, backtrack
26214                self.current = start_pos;
26215                return Ok(None);
26216            }
26217        };
26218
26219        // Parse the string literal
26220        if !self.check(TokenType::String) {
26221            // Backtrack - something went wrong
26222            self.current = start_pos;
26223            return Ok(None);
26224        }
26225
26226        let string_token = self.advance();
26227        let value = Expression::Literal(Literal::String(string_token.text.clone()));
26228
26229        // JSON literal: JSON '"foo"' -> ParseJson expression (matches Python sqlglot)
26230        if matches!(data_type, DataType::Json | DataType::JsonB)
26231            || matches!(type_name.as_str(), "JSON" | "JSONB")
26232        {
26233            return Ok(Some(Expression::ParseJson(Box::new(UnaryFunc {
26234                this: value,
26235                original_name: None,
26236                inferred_type: None,
26237            }))));
26238        }
26239
26240        // Create the Cast expression
26241        Ok(Some(Expression::Cast(Box::new(Cast {
26242            this: value,
26243            to: data_type,
26244            trailing_comments: Vec::new(),
26245            double_colon_syntax: false,
26246            format: None,
26247            default: None,
26248            inferred_type: None,
26249        }))))
26250    }
26251
26252    /// Try to parse type shorthand CAST: INT 1, VARCHAR 'x', STRING 'x', TEXT 'y', etc.
26253    /// In generic mode (no dialect), a type keyword followed by a literal becomes CAST(literal AS type).
26254    /// This matches Python sqlglot's `_parse_types()` behavior.
26255    fn try_parse_type_shorthand_cast(&mut self) -> Result<Option<Expression>> {
26256        // Only apply in generic mode
26257        let is_generic = self.config.dialect.is_none()
26258            || matches!(
26259                self.config.dialect,
26260                Some(crate::dialects::DialectType::Generic)
26261            );
26262        if !is_generic {
26263            return Ok(None);
26264        }
26265
26266        let start_pos = self.current;
26267
26268        // Check if current token is a type keyword
26269        if !self.is_type_keyword() {
26270            return Ok(None);
26271        }
26272
26273        // Don't apply if the type keyword is followed by a left paren (function call)
26274        // or is not followed by a literal
26275        if self.current + 1 >= self.tokens.len() {
26276            return Ok(None);
26277        }
26278
26279        let next_type = self.tokens[self.current + 1].token_type;
26280        // The value after the type keyword must be a literal (number or string)
26281        if !matches!(next_type, TokenType::Number | TokenType::String) {
26282            return Ok(None);
26283        }
26284
26285        // Get the type name
26286        let type_token = self.advance();
26287        let type_name = type_token.text.to_ascii_uppercase();
26288
26289        // Parse the data type
26290        let data_type = match type_name.as_str() {
26291            "INT" | "INTEGER" => DataType::Int {
26292                length: None,
26293                integer_spelling: type_name == "INTEGER",
26294            },
26295            "BIGINT" => DataType::BigInt { length: None },
26296            "SMALLINT" => DataType::SmallInt { length: None },
26297            "TINYINT" => DataType::TinyInt { length: None },
26298            "FLOAT" => DataType::Float {
26299                precision: None,
26300                scale: None,
26301                real_spelling: false,
26302            },
26303            "DOUBLE" => DataType::Double {
26304                precision: None,
26305                scale: None,
26306            },
26307            "DECIMAL" | "NUMERIC" => DataType::Decimal {
26308                precision: None,
26309                scale: None,
26310            },
26311            "REAL" => DataType::Float {
26312                precision: None,
26313                scale: None,
26314                real_spelling: true,
26315            },
26316            "VARCHAR" => DataType::VarChar {
26317                length: None,
26318                parenthesized_length: false,
26319            },
26320            "CHAR" => DataType::Char { length: None },
26321            "TEXT" | "STRING" => DataType::Text,
26322            "BOOLEAN" | "BOOL" => DataType::Boolean,
26323            "BINARY" => DataType::Binary { length: None },
26324            "VARBINARY" => DataType::VarBinary { length: None },
26325            _ => {
26326                // Unknown type, backtrack
26327                self.current = start_pos;
26328                return Ok(None);
26329            }
26330        };
26331
26332        // Parse the literal value
26333        let value = if self.check(TokenType::String) {
26334            let tok = self.advance();
26335            Expression::Literal(Literal::String(tok.text.clone()))
26336        } else if self.check(TokenType::Number) {
26337            let tok = self.advance();
26338            Expression::Literal(Literal::Number(tok.text.clone()))
26339        } else {
26340            self.current = start_pos;
26341            return Ok(None);
26342        };
26343
26344        // Create the Cast expression
26345        Ok(Some(Expression::Cast(Box::new(Cast {
26346            this: value,
26347            to: data_type,
26348            trailing_comments: Vec::new(),
26349            double_colon_syntax: false,
26350            format: None,
26351            default: None,
26352            inferred_type: None,
26353        }))))
26354    }
26355
26356    /// Parse unary expressions
26357    fn parse_unary(&mut self) -> Result<Expression> {
26358        if self.match_token(TokenType::Plus) {
26359            // Unary plus is a no-op - just parse the inner expression
26360            // This handles +++1 -> 1, +-1 -> -1, etc.
26361            self.parse_unary()
26362        } else if self.match_token(TokenType::Dash) {
26363            let expr = self.parse_unary()?;
26364            Ok(Expression::Neg(Box::new(UnaryOp::new(expr))))
26365        } else if self.match_token(TokenType::Plus) {
26366            // Unary plus: +1, +expr — just return the inner expression (no-op)
26367            self.parse_unary()
26368        } else if self.match_token(TokenType::Tilde) {
26369            let expr = self.parse_unary()?;
26370            Ok(Expression::BitwiseNot(Box::new(UnaryOp::new(expr))))
26371        } else if self.match_token(TokenType::DPipeSlash) {
26372            // ||/ (Cube root - PostgreSQL)
26373            let expr = self.parse_unary()?;
26374            Ok(Expression::Cbrt(Box::new(UnaryFunc::with_name(
26375                expr,
26376                "||/".to_string(),
26377            ))))
26378        } else if self.match_token(TokenType::PipeSlash) {
26379            // |/ (Square root - PostgreSQL)
26380            let expr = self.parse_unary()?;
26381            Ok(Expression::Sqrt(Box::new(UnaryFunc::with_name(
26382                expr,
26383                "|/".to_string(),
26384            ))))
26385        } else if self.check(TokenType::DAt)
26386            && matches!(
26387                self.config.dialect,
26388                Some(crate::dialects::DialectType::DuckDB)
26389            )
26390        {
26391            // DuckDB @ operator: @(-1), @(expr), @-1
26392            // @ is the ABS operator in DuckDB with low precedence
26393            // Python sqlglot: "@": lambda self: exp.Abs(this=self._parse_bitwise())
26394            // This means @col + 1 parses as ABS(col + 1), not ABS(col) + 1
26395            self.skip(); // consume @
26396                            // Parse at bitwise level for correct precedence (matches Python sqlglot)
26397            let expr = self.parse_bitwise_or()?;
26398            Ok(Expression::Abs(Box::new(UnaryFunc::new(expr))))
26399        } else if self.check(TokenType::Var)
26400            && self.peek().text.starts_with('@')
26401            && matches!(
26402                self.config.dialect,
26403                Some(crate::dialects::DialectType::DuckDB)
26404            )
26405        {
26406            // DuckDB @ operator with identifier: @col, @col + 1
26407            // Tokenizer creates "@col" as a single Var token, so we need to handle it here
26408            // Python sqlglot: "@": lambda self: exp.Abs(this=self._parse_bitwise())
26409            let token = self.advance(); // consume @col token
26410            let col_name = &token.text[1..]; // strip leading @
26411
26412            // Create column expression for the identifier part
26413            let col_expr = Expression::boxed_column(Column {
26414                name: Identifier::new(col_name),
26415                table: None,
26416                join_mark: false,
26417                trailing_comments: Vec::new(),
26418                span: None,
26419                inferred_type: None,
26420            });
26421
26422            // Check if followed by operators that should be included in the ABS
26423            // We need to parse any remaining operators at bitwise level
26424            // First, check if there's a binary operator after this column
26425            if self.check(TokenType::Plus)
26426                || self.check(TokenType::Dash)
26427                || self.check(TokenType::Star)
26428                || self.check(TokenType::Slash)
26429                || self.check(TokenType::Percent)
26430                || self.check(TokenType::Amp)
26431                || self.check(TokenType::Pipe)
26432                || self.check(TokenType::Caret)
26433                || self.check(TokenType::LtLt)
26434                || self.check(TokenType::GtGt)
26435            {
26436                // There are more operators - we need to continue parsing at bitwise level
26437                // But parse_bitwise_or expects to start fresh, not continue with existing left
26438                // So we use a helper approach: parse_bitwise_continuation
26439                let full_expr = self.parse_bitwise_continuation(col_expr)?;
26440                Ok(Expression::Abs(Box::new(UnaryFunc::new(full_expr))))
26441            } else {
26442                // Just the column, no more operators
26443                Ok(Expression::Abs(Box::new(UnaryFunc::new(col_expr))))
26444            }
26445        } else if self.check(TokenType::DAt)
26446            && (self.check_next(TokenType::LParen) || self.check_next(TokenType::Dash))
26447        {
26448            // Non-DuckDB dialects: only handle @(expr) and @-expr as ABS
26449            self.skip(); // consume @
26450            let expr = self.parse_bitwise_or()?;
26451            Ok(Expression::Abs(Box::new(UnaryFunc::new(expr))))
26452        } else if self.check(TokenType::Prior)
26453            && !self.check_next(TokenType::As)
26454            && !self.check_next(TokenType::Comma)
26455            && !self.check_next(TokenType::RParen)
26456            && !self.check_next(TokenType::Semicolon)
26457            && self.current + 1 < self.tokens.len()
26458        {
26459            // Oracle PRIOR expression - references parent row's value in hierarchical queries
26460            // Can appear in SELECT list, CONNECT BY, or other expression contexts
26461            // Python sqlglot: "PRIOR": lambda self: self.expression(exp.Prior, this=self._parse_bitwise())
26462            // When followed by AS/comma/rparen/end, treat PRIOR as an identifier (column name)
26463            self.skip(); // consume PRIOR
26464            let expr = self.parse_bitwise_or()?;
26465            Ok(Expression::Prior(Box::new(Prior { this: expr })))
26466        } else {
26467            // Try to parse type literals like: point '(4,4)', timestamp '2024-01-01', interval '1 day'
26468            // PostgreSQL allows type name followed by string literal as a cast shorthand
26469            if let Some(type_literal) = self.try_parse_type_literal()? {
26470                return self.parse_postfix_operators(type_literal);
26471            }
26472            // Try to parse type shorthand CAST: INT 1, VARCHAR 'x', STRING 'x', TEXT 'y', etc.
26473            // In generic mode, type keyword followed by literal -> CAST(literal AS type)
26474            if let Some(type_cast) = self.try_parse_type_shorthand_cast()? {
26475                return self.parse_postfix_operators(type_cast);
26476            }
26477            let expr = self.parse_primary()?;
26478            // Handle postfix exclamation mark for Snowflake model attribute syntax: model!PREDICT(...)
26479            self.parse_postfix_operators(expr)
26480        }
26481    }
26482
26483    /// Parse postfix operators like ! (model attribute in Snowflake) and : (JSON path in Snowflake)
26484    fn parse_postfix_operators(&mut self, mut expr: Expression) -> Result<Expression> {
26485        // Handle Oracle/Redshift outer join marker (+) after column reference
26486        // Syntax: column_ref (+) indicates optional side of join
26487        if self.check(TokenType::LParen) && self.check_next(TokenType::Plus) {
26488            // Look ahead to verify it's ( + )
26489            let saved_pos = self.current;
26490            if self.match_token(TokenType::LParen)
26491                && self.match_token(TokenType::Plus)
26492                && self.match_token(TokenType::RParen)
26493            {
26494                // Set join_mark on the column expression
26495                if let Expression::Column(ref mut col) = expr {
26496                    col.join_mark = true;
26497                }
26498            } else {
26499                self.current = saved_pos;
26500            }
26501        }
26502
26503        // Handle EXCLAMATION for Snowflake model attribute syntax: model!PREDICT(...)
26504        while self.match_token(TokenType::Exclamation) {
26505            // Parse the attribute/function after the exclamation mark
26506            // This can be either a simple identifier (model!admin) or a function call (model!PREDICT(1))
26507            let attr = self.parse_primary()?;
26508            expr = Expression::ModelAttribute(Box::new(ModelAttribute {
26509                this: Box::new(expr),
26510                expression: Box::new(attr),
26511            }));
26512        }
26513
26514        // Handle COLON for Snowflake JSON path extraction: a:field or a:field.subfield
26515        // This creates JSONExtract expressions that transform to GET_PATH(a, 'field') in Snowflake
26516        expr = self.parse_colon_json_path(expr)?;
26517
26518        // Handle DCOLON (::) - in SingleStore it's JSON extraction, in other dialects it's cast
26519        // SingleStore JSON path syntax:
26520        //   a::b -> JSON_EXTRACT_JSON(a, 'b')
26521        //   a::$b -> JSON_EXTRACT_STRING(a, 'b')
26522        //   a::%b -> JSON_EXTRACT_DOUBLE(a, 'b')
26523        //   a::?names -> JSON match syntax
26524        if matches!(
26525            self.config.dialect,
26526            Some(crate::dialects::DialectType::SingleStore)
26527        ) {
26528            expr = self.parse_singlestore_json_path(expr)?;
26529        } else {
26530            // For other dialects, :: is cast syntax
26531            // IMPORTANT: Use parse_data_type_for_cast to avoid consuming subscripts as array dimensions
26532            // e.g., ::VARIANT[0] should be cast to VARIANT followed by subscript [0]
26533            while self.match_token(TokenType::DColon) {
26534                let data_type = self.parse_data_type_for_cast()?;
26535                expr = Expression::Cast(Box::new(Cast {
26536                    this: expr,
26537                    to: data_type,
26538                    trailing_comments: Vec::new(),
26539                    double_colon_syntax: true,
26540                    format: None,
26541                    default: None,
26542                    inferred_type: None,
26543                }));
26544            }
26545        }
26546
26547        // Teradata: (FORMAT '...') phrase after an expression
26548        if matches!(
26549            self.config.dialect,
26550            Some(crate::dialects::DialectType::Teradata)
26551        ) && self.check(TokenType::LParen)
26552            && self.check_next(TokenType::Format)
26553        {
26554            self.skip(); // consume (
26555            self.skip(); // consume FORMAT
26556            let format = self.expect_string()?;
26557            self.expect(TokenType::RParen)?;
26558            expr = Expression::FormatPhrase(Box::new(FormatPhrase {
26559                this: Box::new(expr),
26560                format,
26561            }));
26562        }
26563
26564        Ok(expr)
26565    }
26566
26567    /// Parse SingleStore JSON path extraction syntax
26568    /// Examples:
26569    ///   a::b -> JSON_EXTRACT_JSON(a, 'b')
26570    ///   a::$b -> JSON_EXTRACT_STRING(a, 'b')
26571    ///   a::%b -> JSON_EXTRACT_DOUBLE(a, 'b')
26572    ///   a::`b`::`2` -> nested JSON extraction
26573    fn parse_singlestore_json_path(&mut self, mut expr: Expression) -> Result<Expression> {
26574        loop {
26575            if self.match_token(TokenType::DColon) {
26576                // :: followed by identifier -> JSON_EXTRACT_JSON
26577                // Check if next is a backtick-quoted identifier or regular identifier
26578                let path_key = if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
26579                    self.advance().text
26580                } else if self.check(TokenType::Number) {
26581                    // a::2 -> JSON_EXTRACT_JSON(a, '2')
26582                    self.advance().text
26583                } else {
26584                    return Err(self.parse_error("Expected identifier after ::"));
26585                };
26586
26587                expr = Expression::Function(Box::new(Function::new(
26588                    "JSON_EXTRACT_JSON".to_string(),
26589                    vec![expr, Expression::string(&path_key)],
26590                )));
26591            } else if self.match_token(TokenType::DColonDollar) {
26592                // ::$ followed by identifier -> JSON_EXTRACT_STRING
26593                let path_key = if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
26594                    self.advance().text
26595                } else {
26596                    return Err(self.parse_error("Expected identifier after ::$"));
26597                };
26598
26599                expr = Expression::Function(Box::new(Function::new(
26600                    "JSON_EXTRACT_STRING".to_string(),
26601                    vec![expr, Expression::string(&path_key)],
26602                )));
26603            } else if self.match_token(TokenType::DColonPercent) {
26604                // ::% followed by identifier -> JSON_EXTRACT_DOUBLE
26605                let path_key = if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
26606                    self.advance().text
26607                } else {
26608                    return Err(self.parse_error("Expected identifier after ::%"));
26609                };
26610
26611                expr = Expression::Function(Box::new(Function::new(
26612                    "JSON_EXTRACT_DOUBLE".to_string(),
26613                    vec![expr, Expression::string(&path_key)],
26614                )));
26615            } else if self.match_token(TokenType::DColonQMark) {
26616                // ::? followed by identifier -> Keep as JSONMatchAny expression for now
26617                let path_key = if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
26618                    self.advance().text
26619                } else {
26620                    return Err(self.parse_error("Expected identifier after ::?"));
26621                };
26622
26623                // For now, create a function that will be handled specially
26624                expr = Expression::Function(Box::new(Function::new(
26625                    "JSON_EXTRACT_JSON".to_string(), // placeholder
26626                    vec![expr, Expression::string(&format!("?{}", path_key))],
26627                )));
26628            } else {
26629                break;
26630            }
26631        }
26632        Ok(expr)
26633    }
26634
26635    /// Parse colon-separated JSON path syntax (Snowflake variant extraction)
26636    /// Examples:
26637    ///   a:from -> GET_PATH(a, 'from')
26638    ///   a:b.c.d -> GET_PATH(a, 'b.c.d')
26639    ///   a:from::STRING -> CAST(GET_PATH(a, 'from') AS VARCHAR)
26640    ///   a:b:c.d -> GET_PATH(a, 'b.c.d') (multiple colons joined into single path)
26641    fn parse_colon_json_path(&mut self, this: Expression) -> Result<Expression> {
26642        // DuckDB uses colon for prefix alias syntax (e.g., "alias: expr" means "expr AS alias")
26643        // Skip JSON path extraction for DuckDB - it's handled separately in parse_select_expressions
26644        if matches!(
26645            self.config.dialect,
26646            Some(crate::dialects::DialectType::DuckDB)
26647        ) {
26648            return Ok(this);
26649        }
26650
26651        // ClickHouse uses : as part of the ternary operator (condition ? true : false)
26652        // Skip JSON path extraction for ClickHouse to avoid consuming the ternary separator
26653        if matches!(
26654            self.config.dialect,
26655            Some(crate::dialects::DialectType::ClickHouse)
26656        ) {
26657            return Ok(this);
26658        }
26659
26660        // Only apply colon JSON path parsing to identifiers, columns, and function results
26661        // This prevents {'key': 'value'} object literals from being misinterpreted
26662        let is_valid_json_path_base = matches!(
26663            &this,
26664            Expression::Column(_) |
26665            Expression::Identifier(_) |
26666            Expression::Dot(_) |
26667            Expression::JSONExtract(_) |  // Allow chained paths like a:b:c
26668            Expression::Function(_) |     // Allow function results like PARSE_JSON(...):x
26669            Expression::ParseJson(_) |    // Allow PARSE_JSON specifically
26670            Expression::Parameter(_) // Allow positional params like $1:name
26671        );
26672
26673        if !is_valid_json_path_base {
26674            return Ok(this);
26675        }
26676
26677        // Check if we have a colon (but NOT double-colon which is cast syntax)
26678        if !self.check(TokenType::Colon) {
26679            return Ok(this);
26680        }
26681
26682        // Make sure this is not a double-colon (::) which is cast syntax
26683        if self.check_next(TokenType::Colon) {
26684            // This is :: (DColon should have been tokenized, but just in case)
26685            return Ok(this);
26686        }
26687
26688        // Collect ALL the JSON path parts across multiple colons
26689        // a:b.c:d.e -> GET_PATH(a, 'b.c.d.e')
26690        // a:b[0].c -> GET_PATH(a, 'b[0].c')
26691        let mut path_string = String::new();
26692
26693        // Parse all colon-separated path segments
26694        while self.check(TokenType::Colon) && !self.check_next(TokenType::Colon) {
26695            // Save position before consuming colon so we can backtrack
26696            // if what follows isn't a valid JSON path component (e.g., DuckDB's "foo: 1" label syntax)
26697            let saved_pos = self.current;
26698            let saved_path_len = path_string.len();
26699
26700            // Consume the colon
26701            self.skip();
26702
26703            // Parse first path component (required) - can be any identifier including keywords
26704            // Also handle backtick-quoted identifiers like `zip code` or `fb:testid`
26705            // Also handle bracket notation directly after colon: c1:['price'] or c1:["foo bar"]
26706            // IMPORTANT: Check QuotedIdentifier FIRST since is_identifier_token() includes QuotedIdentifier
26707            let mut had_initial_component = false;
26708            if self.check(TokenType::QuotedIdentifier) {
26709                // Quoted field name in variant access
26710                // Snowflake: v:"fruit" → double-quoted key → stored as plain text 'fruit'
26711                // Databricks: raw:`zip code` → backtick-quoted key → stored as bracket notation '["zip code"]'
26712                let quoted_name = self.advance().text.clone();
26713                let is_snowflake = matches!(
26714                    self.config.dialect,
26715                    Some(crate::dialects::DialectType::Snowflake)
26716                );
26717                let needs_bracket = quoted_name.contains(' ') || quoted_name.contains('\'');
26718                if is_snowflake && !needs_bracket {
26719                    // Snowflake double-quoted keys without special chars are stored as plain text
26720                    // Add dot separator for plain segments
26721                    if !path_string.is_empty() {
26722                        path_string.push('.');
26723                    }
26724                    path_string.push_str(&quoted_name);
26725                } else if is_snowflake && needs_bracket {
26726                    // Snowflake keys with spaces/apostrophes use bracket notation: ["key with spaces"]
26727                    // No dot before bracket notation
26728                    path_string.push_str("[\"");
26729                    // Don't escape single quotes here - the generator will handle escaping
26730                    // when outputting the string literal
26731                    path_string.push_str(&quoted_name);
26732                    path_string.push_str("\"]");
26733                } else {
26734                    // Other dialects (Databricks): wrap in bracket notation
26735                    // No dot before bracket notation
26736                    path_string.push_str("[\"");
26737                    for c in quoted_name.chars() {
26738                        if c == '"' {
26739                            path_string.push_str("\\\"");
26740                        } else {
26741                            path_string.push(c);
26742                        }
26743                    }
26744                    path_string.push_str("\"]");
26745                }
26746                had_initial_component = true;
26747            } else if self.is_identifier_token()
26748                || self.is_safe_keyword_as_identifier()
26749                || self.is_reserved_keyword_as_identifier()
26750            {
26751                // Add a dot separator for plain identifier segments
26752                if !path_string.is_empty() {
26753                    path_string.push('.');
26754                }
26755                let first_part = self.advance().text;
26756                path_string.push_str(&first_part);
26757                had_initial_component = true;
26758            } else if self.check(TokenType::LBracket) {
26759                // Bracket notation directly after colon: c1:['price'] or c1:["foo bar"]
26760                // Mark that we have a valid path start - the bracket will be parsed in the loop below
26761                had_initial_component = true;
26762            }
26763
26764            if !had_initial_component {
26765                // Not a valid JSON path component - backtrack and stop
26766                // This handles cases like DuckDB's "foo: 1" label/alias syntax
26767                // where the colon is followed by a non-identifier (e.g., a number)
26768                self.current = saved_pos;
26769                path_string.truncate(saved_path_len);
26770                break;
26771            }
26772
26773            // Parse optional array indices and additional path components
26774            loop {
26775                // Handle array index: [0], [1], [*], ['key'], ["key"], etc.
26776                if self.match_token(TokenType::LBracket) {
26777                    // Parse the index expression (typically a number, identifier, * for wildcard, or string key)
26778                    if self.check(TokenType::Number) {
26779                        path_string.push('[');
26780                        let idx = self.advance().text;
26781                        path_string.push_str(&idx);
26782                        self.expect(TokenType::RBracket)?;
26783                        path_string.push(']');
26784                    } else if self.check(TokenType::Star) {
26785                        // Wildcard array access: [*] matches all array elements
26786                        path_string.push('[');
26787                        self.skip();
26788                        path_string.push('*');
26789                        self.expect(TokenType::RBracket)?;
26790                        path_string.push(']');
26791                    } else if self.check(TokenType::String) {
26792                        // Single-quoted string key access: ['bicycle']
26793                        // Convert to dot notation for simple keys, keep bracket notation for keys with spaces
26794                        let key = self.advance().text;
26795                        self.expect(TokenType::RBracket)?;
26796                        // Check if the key contains spaces or special characters that require bracket notation
26797                        let needs_brackets =
26798                            key.contains(' ') || key.contains('"') || key.contains('\'');
26799                        if needs_brackets {
26800                            // Keep bracket notation with double quotes: ["zip code"]
26801                            path_string.push_str("[\"");
26802                            for c in key.chars() {
26803                                if c == '"' {
26804                                    path_string.push_str("\\\"");
26805                                } else {
26806                                    path_string.push(c);
26807                                }
26808                            }
26809                            path_string.push_str("\"]");
26810                        } else {
26811                            // Convert to dot notation: store['bicycle'] -> store.bicycle
26812                            // But only add dot if path_string is not empty (handles c1:['price'] -> c1:price)
26813                            if !path_string.is_empty() {
26814                                path_string.push('.');
26815                            }
26816                            path_string.push_str(&key);
26817                        }
26818                    } else if self.check(TokenType::QuotedIdentifier) {
26819                        // Double-quoted string key access: ["zip code"]
26820                        // These are tokenized as QuotedIdentifier, not String
26821                        // Must be checked BEFORE is_identifier_token() since it includes QuotedIdentifier
26822                        let key = self.advance().text;
26823                        self.expect(TokenType::RBracket)?;
26824                        // Always use bracket notation with double quotes for quoted identifiers
26825                        path_string.push_str("[\"");
26826                        for c in key.chars() {
26827                            if c == '"' {
26828                                path_string.push_str("\\\"");
26829                            } else {
26830                                path_string.push(c);
26831                            }
26832                        }
26833                        path_string.push_str("\"]");
26834                    } else if self.is_identifier_token() {
26835                        path_string.push('[');
26836                        let idx = self.advance().text;
26837                        path_string.push_str(&idx);
26838                        self.expect(TokenType::RBracket)?;
26839                        path_string.push(']');
26840                    } else {
26841                        // Empty brackets or unexpected token - just close the bracket
26842                        path_string.push('[');
26843                        self.expect(TokenType::RBracket)?;
26844                        path_string.push(']');
26845                    }
26846                } else if self.match_token(TokenType::Dot) {
26847                    // Handle dot access
26848                    path_string.push('.');
26849                    if self.is_identifier_token()
26850                        || self.is_safe_keyword_as_identifier()
26851                        || self.is_reserved_keyword_as_identifier()
26852                    {
26853                        let part = self.advance().text;
26854                        path_string.push_str(&part);
26855                    } else {
26856                        return Err(self.parse_error("Expected identifier after . in JSON path"));
26857                    }
26858                } else {
26859                    break;
26860                }
26861            }
26862        }
26863
26864        // If no path was parsed (e.g., backtracked on first colon), return the original expression
26865        if path_string.is_empty() {
26866            return Ok(this);
26867        }
26868
26869        // Create the JSONExtract expression with variant_extract marker
26870        let path_expr = Expression::Literal(Literal::String(path_string));
26871        let json_extract = Expression::JSONExtract(Box::new(JSONExtract {
26872            this: Box::new(this),
26873            expression: Box::new(path_expr),
26874            only_json_types: None,
26875            expressions: Vec::new(),
26876            variant_extract: Some(Box::new(Expression::Boolean(BooleanLiteral {
26877                value: true,
26878            }))),
26879            json_query: None,
26880            option: None,
26881            quote: None,
26882            on_condition: None,
26883            requires_json: None,
26884        }));
26885
26886        Ok(json_extract)
26887    }
26888
26889    /// Check if the current token is a reserved keyword that can be used as identifier in JSON path
26890    fn is_reserved_keyword_as_identifier(&self) -> bool {
26891        if self.is_at_end() {
26892            return false;
26893        }
26894        let token = self.peek();
26895        // Allow reserved keywords like FROM, SELECT, etc. as JSON path components
26896        matches!(
26897            token.token_type,
26898            TokenType::From
26899                | TokenType::Select
26900                | TokenType::Where
26901                | TokenType::And
26902                | TokenType::Or
26903                | TokenType::Not
26904                | TokenType::In
26905                | TokenType::As
26906                | TokenType::On
26907                | TokenType::Join
26908                | TokenType::Left
26909                | TokenType::Right
26910                | TokenType::Inner
26911                | TokenType::Outer
26912                | TokenType::Cross
26913                | TokenType::Full
26914                | TokenType::Group
26915                | TokenType::Order
26916                | TokenType::By
26917                | TokenType::Having
26918                | TokenType::Limit
26919                | TokenType::Offset
26920                | TokenType::Union
26921                | TokenType::Except
26922                | TokenType::Intersect
26923                | TokenType::All
26924                | TokenType::Distinct
26925                | TokenType::Case
26926                | TokenType::When
26927                | TokenType::Then
26928                | TokenType::Else
26929                | TokenType::End
26930                | TokenType::Null
26931                | TokenType::True
26932                | TokenType::False
26933                | TokenType::Between
26934                | TokenType::Like
26935                | TokenType::Is
26936                | TokenType::Exists
26937                | TokenType::Insert
26938                | TokenType::Update
26939                | TokenType::Delete
26940                | TokenType::Create
26941                | TokenType::Alter
26942                | TokenType::Drop
26943                | TokenType::Table
26944                | TokenType::View
26945                | TokenType::Index
26946                | TokenType::Set
26947                | TokenType::Values
26948                | TokenType::Into
26949                | TokenType::Default
26950                | TokenType::Key
26951                | TokenType::Unique
26952                | TokenType::Check
26953                | TokenType::Constraint
26954                | TokenType::References
26955        )
26956    }
26957
26958    /// Parse primary expressions
26959    fn parse_primary(&mut self) -> Result<Expression> {
26960        // Handle APPROXIMATE COUNT(DISTINCT expr) - Redshift syntax
26961        // Parses as ApproxDistinct expression
26962        if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("APPROXIMATE") {
26963            let saved_pos = self.current;
26964            self.skip(); // consume APPROXIMATE
26965                            // Parse the COUNT(DISTINCT ...) that follows
26966            let func = self.parse_primary()?;
26967            // Check if it's COUNT with DISTINCT
26968            if let Expression::Count(ref count_expr) = func {
26969                if count_expr.distinct {
26970                    let this_expr = count_expr.this.clone().unwrap_or_else(|| {
26971                        Expression::Star(crate::expressions::Star {
26972                            table: None,
26973                            except: None,
26974                            replace: None,
26975                            rename: None,
26976                            trailing_comments: Vec::new(),
26977                            span: None,
26978                        })
26979                    });
26980                    return Ok(Expression::ApproxDistinct(Box::new(
26981                        crate::expressions::AggFunc {
26982                            this: this_expr,
26983                            distinct: false,
26984                            filter: None,
26985                            order_by: Vec::new(),
26986                            name: Some("APPROX_DISTINCT".to_string()),
26987                            ignore_nulls: None,
26988                            having_max: None,
26989                            limit: None,
26990                            inferred_type: None,
26991                        },
26992                    )));
26993                }
26994            }
26995            // Not COUNT(DISTINCT ...) - backtrack
26996            self.current = saved_pos;
26997        }
26998
26999        if let Some(connect_by_root) = self.try_parse_connect_by_root_expression()? {
27000            return Ok(connect_by_root);
27001        }
27002
27003        // PostgreSQL VARIADIC prefix in function call arguments
27004        // e.g., SELECT MLEAST(VARIADIC ARRAY[10, -1, 5, 4.4])
27005        if matches!(
27006            self.config.dialect,
27007            Some(crate::dialects::DialectType::PostgreSQL)
27008                | Some(crate::dialects::DialectType::Redshift)
27009        ) {
27010            if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("VARIADIC") {
27011                self.skip(); // consume VARIADIC
27012                let expr = self.parse_bitwise_or()?;
27013                return Ok(Expression::Variadic(Box::new(
27014                    crate::expressions::Variadic {
27015                        this: Box::new(expr),
27016                    },
27017                )));
27018            }
27019        }
27020
27021        // MySQL charset introducer: _utf8mb4 'string', _latin1 x'hex', etc.
27022        if matches!(
27023            self.config.dialect,
27024            Some(crate::dialects::DialectType::MySQL)
27025                | Some(crate::dialects::DialectType::SingleStore)
27026                | Some(crate::dialects::DialectType::Doris)
27027                | Some(crate::dialects::DialectType::StarRocks)
27028        ) {
27029            if self.check(TokenType::Var) || self.check(TokenType::Identifier) {
27030                if self.peek().text.starts_with('_') && Self::is_mysql_charset_introducer(&self.peek().text.to_ascii_uppercase()) {
27031                    // Check if next token is a string literal or hex string
27032                    if self.current + 1 < self.tokens.len() {
27033                        let next_tt = self.tokens[self.current + 1].token_type;
27034                        if matches!(
27035                            next_tt,
27036                            TokenType::String | TokenType::HexString | TokenType::BitString
27037                        ) {
27038                            let charset_token = self.advance(); // consume charset name
27039                            let charset_name = charset_token.text.clone();
27040                            let literal = self.parse_primary()?; // parse the string/hex literal
27041                            return Ok(Expression::Introducer(Box::new(
27042                                crate::expressions::Introducer {
27043                                    this: Box::new(Expression::Column(Box::new(
27044                                        crate::expressions::Column {
27045                                            name: crate::expressions::Identifier {
27046                                                name: charset_name,
27047                                                quoted: false,
27048                                                trailing_comments: Vec::new(),
27049                                                span: None,
27050                                            },
27051                                            table: None,
27052                                            join_mark: false,
27053                                            trailing_comments: Vec::new(),
27054                                            span: None,
27055                                            inferred_type: None,
27056                                        },
27057                                    ))),
27058                                    expression: Box::new(literal),
27059                                },
27060                            )));
27061                        }
27062                    }
27063                }
27064            }
27065        }
27066
27067        // Array literal: [1, 2, 3] or comprehension: [expr FOR var IN iterator]
27068        if self.match_token(TokenType::LBracket) {
27069            // Parse empty array: []
27070            if self.match_token(TokenType::RBracket) {
27071                return Ok(Expression::ArrayFunc(Box::new(ArrayConstructor {
27072                    expressions: Vec::new(),
27073                    bracket_notation: true,
27074                    use_list_keyword: false,
27075                })));
27076            }
27077
27078            // Parse first expression
27079            let first_expr = self.parse_expression()?;
27080
27081            // Check for comprehension syntax: [expr FOR var IN iterator [IF condition]]
27082            if self.match_token(TokenType::For) {
27083                // Parse loop variable - typically a simple identifier like 'x'
27084                let loop_var = self.parse_primary()?;
27085
27086                // Parse optional position (second variable after comma)
27087                let position = if self.match_token(TokenType::Comma) {
27088                    Some(self.parse_primary()?)
27089                } else {
27090                    None
27091                };
27092
27093                // Expect IN keyword
27094                if !self.match_token(TokenType::In) {
27095                    return Err(self.parse_error("Expected IN in comprehension"));
27096                }
27097
27098                // Parse iterator expression
27099                let iterator = self.parse_expression()?;
27100
27101                // Parse optional condition after IF
27102                let condition = if self.match_token(TokenType::If) {
27103                    Some(self.parse_expression()?)
27104                } else {
27105                    None
27106                };
27107
27108                // Expect closing bracket
27109                self.expect(TokenType::RBracket)?;
27110
27111                // Return Comprehension
27112                return Ok(Expression::Comprehension(Box::new(Comprehension {
27113                    this: Box::new(first_expr),
27114                    expression: Box::new(loop_var),
27115                    position: position.map(Box::new),
27116                    iterator: Some(Box::new(iterator)),
27117                    condition: condition.map(Box::new),
27118                })));
27119            }
27120
27121            // Regular array - continue parsing elements
27122            // ClickHouse allows AS aliases in array: [1 AS a, 2 AS b]
27123            let first_expr = if matches!(
27124                self.config.dialect,
27125                Some(crate::dialects::DialectType::ClickHouse)
27126            ) && self.check(TokenType::As)
27127                && !self.check_next(TokenType::RBracket)
27128            {
27129                self.skip(); // consume AS
27130                let alias = self.expect_identifier()?;
27131                Expression::Alias(Box::new(Alias::new(first_expr, Identifier::new(alias))))
27132            } else {
27133                first_expr
27134            };
27135            let mut expressions = vec![first_expr];
27136            while self.match_token(TokenType::Comma) {
27137                // Handle trailing comma
27138                if self.check(TokenType::RBracket) {
27139                    break;
27140                }
27141                let expr = self.parse_expression()?;
27142                // ClickHouse: handle AS alias on array elements
27143                let expr = if matches!(
27144                    self.config.dialect,
27145                    Some(crate::dialects::DialectType::ClickHouse)
27146                ) && self.check(TokenType::As)
27147                    && !self.check_next(TokenType::RBracket)
27148                {
27149                    self.skip(); // consume AS
27150                    let alias = self.expect_identifier()?;
27151                    Expression::Alias(Box::new(Alias::new(expr, Identifier::new(alias))))
27152                } else {
27153                    expr
27154                };
27155                expressions.push(expr);
27156            }
27157            self.expect(TokenType::RBracket)?;
27158            return self.maybe_parse_subscript(Expression::ArrayFunc(Box::new(ArrayConstructor {
27159                expressions,
27160                bracket_notation: true,
27161                use_list_keyword: false,
27162            })));
27163        }
27164
27165        // Map/Struct literal with curly braces: {'a': 1, 'b': 2}
27166        // Or Snowflake wildcard syntax: {*}, {tbl.*}, {* EXCLUDE (...)}, {* ILIKE '...'}
27167        if self.match_token(TokenType::LBrace) {
27168            // ClickHouse query parameter: {name: Type}
27169            // We consumed `{` above, so rewind and let the dedicated parser consume it.
27170            if matches!(
27171                self.config.dialect,
27172                Some(crate::dialects::DialectType::ClickHouse)
27173            ) {
27174                self.current -= 1;
27175                if let Some(param) = self.parse_clickhouse_braced_parameter()? {
27176                    return self.maybe_parse_subscript(param);
27177                }
27178                // Not a ClickHouse query parameter, restore position after `{` for map/wildcard parsing.
27179                self.current += 1;
27180            }
27181
27182            // Parse empty map: {}
27183            if self.match_token(TokenType::RBrace) {
27184                return self.maybe_parse_subscript(Expression::MapFunc(Box::new(MapConstructor {
27185                    keys: Vec::new(),
27186                    values: Vec::new(),
27187                    curly_brace_syntax: true,
27188                    with_map_keyword: false,
27189                })));
27190            }
27191
27192            // Check for ODBC escape syntax: {fn function_name(args)}
27193            // This must be checked before wildcards and map literals
27194            if self.check_identifier("fn") {
27195                self.skip(); // consume 'fn'
27196                                // Parse function call
27197                let func_name = self.expect_identifier_or_keyword_with_quoted()?;
27198                self.expect(TokenType::LParen)?;
27199
27200                // Parse function arguments
27201                let mut args = Vec::new();
27202                if !self.check(TokenType::RParen) {
27203                    loop {
27204                        args.push(self.parse_expression()?);
27205                        if !self.match_token(TokenType::Comma) {
27206                            break;
27207                        }
27208                    }
27209                }
27210                self.expect(TokenType::RParen)?;
27211                self.expect(TokenType::RBrace)?;
27212
27213                // Return as a regular function call (the ODBC escape is just syntax sugar)
27214                return Ok(Expression::Function(Box::new(Function::new(
27215                    func_name.name,
27216                    args,
27217                ))));
27218            }
27219
27220            // Check for ODBC datetime literals: {d'2024-01-01'}, {t'12:00:00'}, {ts'2024-01-01 12:00:00'}
27221            if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
27222                let type_text = self.peek().text.to_lowercase();
27223                if (type_text == "d" || type_text == "t" || type_text == "ts")
27224                    && self.check_next(TokenType::String)
27225                {
27226                    self.skip(); // consume type indicator (d, t, or ts)
27227                    let value = self.expect_string()?;
27228                    self.expect(TokenType::RBrace)?;
27229
27230                    // Return appropriate expression based on type
27231                    return match type_text.as_str() {
27232                        "d" => Ok(Expression::Date(Box::new(
27233                            crate::expressions::UnaryFunc::new(Expression::Literal(
27234                                crate::expressions::Literal::String(value),
27235                            )),
27236                        ))),
27237                        "t" => Ok(Expression::Time(Box::new(
27238                            crate::expressions::UnaryFunc::new(Expression::Literal(
27239                                crate::expressions::Literal::String(value),
27240                            )),
27241                        ))),
27242                        "ts" => Ok(Expression::Timestamp(Box::new(
27243                            crate::expressions::TimestampFunc {
27244                                this: Some(Box::new(Expression::Literal(
27245                                    crate::expressions::Literal::String(value),
27246                                ))),
27247                                zone: None,
27248                                with_tz: None,
27249                                safe: None,
27250                            },
27251                        ))),
27252                        _ => {
27253                            Err(self
27254                                .parse_error(format!("Unknown ODBC datetime type: {}", type_text)))
27255                        }
27256                    };
27257                }
27258            }
27259
27260            // Check for Snowflake wildcard syntax: {*}, {tbl.*}, {* EXCLUDE (...)}, {* ILIKE '...'}
27261            // Pattern: either {*...} or {identifier/var followed by .*}
27262            // Note: Identifiers may be tokenized as Var or Identifier
27263            let is_table_star = (self.check(TokenType::Identifier) || self.check(TokenType::Var))
27264                && self.check_next(TokenType::Dot)
27265                && self
27266                    .tokens
27267                    .get(self.current + 2)
27268                    .map(|t| t.token_type == TokenType::Star)
27269                    .unwrap_or(false);
27270            let is_wildcard = self.check(TokenType::Star) || is_table_star;
27271
27272            if is_wildcard {
27273                // Parse the wildcard expression
27274                let wildcard_expr = if self.match_token(TokenType::Star) {
27275                    // {*} or {* EXCLUDE ...} or {* ILIKE ...}
27276                    // Check for ILIKE first since it's different from standard star modifiers
27277                    if self.check_keyword_text("ILIKE") {
27278                        self.skip();
27279                        let pattern = self.parse_expression()?;
27280                        // Create an ILike expression with Star as left side
27281                        Expression::ILike(Box::new(LikeOp {
27282                            left: Expression::Star(Star {
27283                                table: None,
27284                                except: None,
27285                                replace: None,
27286                                rename: None,
27287                                trailing_comments: Vec::new(),
27288                                span: None,
27289                            }),
27290                            right: pattern,
27291                            escape: None,
27292                            quantifier: None,
27293                            inferred_type: None,
27294                        }))
27295                    } else {
27296                        // {*} or {* EXCLUDE ...}
27297                        let star = self.parse_star_modifiers(None)?;
27298                        Expression::Star(star)
27299                    }
27300                } else {
27301                    // {tbl.*} - table qualified wildcard
27302                    let table_name = self.expect_identifier_or_keyword_with_quoted()?;
27303                    self.expect(TokenType::Dot)?;
27304                    self.expect(TokenType::Star)?;
27305                    let star = self.parse_star_modifiers(Some(table_name))?;
27306                    Expression::Star(star)
27307                };
27308
27309                self.expect(TokenType::RBrace)?;
27310
27311                // Wrap in BracedWildcard for generation
27312                return Ok(Expression::BracedWildcard(Box::new(wildcard_expr)));
27313            }
27314
27315            // Parse key-value pairs: key: value, ...
27316            let mut keys = Vec::new();
27317            let mut values = Vec::new();
27318            loop {
27319                let key = self.parse_expression()?;
27320                self.expect(TokenType::Colon)?;
27321                let value = self.parse_expression()?;
27322                keys.push(key);
27323                values.push(value);
27324                if !self.match_token(TokenType::Comma) {
27325                    break;
27326                }
27327                // Handle trailing comma
27328                if self.check(TokenType::RBrace) {
27329                    break;
27330                }
27331            }
27332            self.expect(TokenType::RBrace)?;
27333            return self.maybe_parse_subscript(Expression::MapFunc(Box::new(MapConstructor {
27334                keys,
27335                values,
27336                curly_brace_syntax: true,
27337                with_map_keyword: false,
27338            })));
27339        }
27340
27341        // Parenthesized expression or subquery
27342        if self.match_token(TokenType::LParen) {
27343            // Capture comments from the ( token (e.g., "(/* comment */ 1)")
27344            let lparen_comments = self.previous_trailing_comments().to_vec();
27345
27346            // Empty parens () — could be empty tuple or zero-param lambda () -> body
27347            if self.check(TokenType::RParen) {
27348                self.skip(); // consume )
27349                                // Check for lambda: () -> body
27350                if self.match_token(TokenType::Arrow) || self.match_token(TokenType::FArrow) {
27351                    let body = self.parse_expression()?;
27352                    return Ok(Expression::Lambda(Box::new(LambdaExpr {
27353                        parameters: Vec::new(),
27354                        body,
27355                        colon: false,
27356                        parameter_types: Vec::new(),
27357                    })));
27358                }
27359                // Otherwise empty tuple
27360                return self.maybe_parse_subscript(Expression::Tuple(Box::new(Tuple {
27361                    expressions: Vec::new(),
27362                })));
27363            }
27364
27365            // Check if this is a VALUES expression inside parens: (VALUES ...)
27366            if self.check(TokenType::Values) {
27367                let values = self.parse_values()?;
27368                self.expect(TokenType::RParen)?;
27369                return Ok(Expression::Subquery(Box::new(Subquery {
27370                    this: values,
27371                    alias: None,
27372                    column_aliases: Vec::new(),
27373                    order_by: None,
27374                    limit: None,
27375                    offset: None,
27376                    distribute_by: None,
27377                    sort_by: None,
27378                    cluster_by: None,
27379                    lateral: false,
27380                    modifiers_inside: false,
27381                    trailing_comments: self.previous_trailing_comments().to_vec(),
27382                    inferred_type: None,
27383                })));
27384            }
27385
27386            // Check if this is a subquery (SELECT, WITH, DuckDB FROM-first, or ClickHouse EXPLAIN)
27387            let is_explain_subquery = self.check(TokenType::Var)
27388                && self.peek().text.eq_ignore_ascii_case("EXPLAIN")
27389                && self.peek_nth(1).map_or(false, |t| {
27390                    // EXPLAIN followed by statement/style keywords is a subquery
27391                    matches!(
27392                        t.token_type,
27393                        TokenType::Select
27394                            | TokenType::Insert
27395                            | TokenType::Create
27396                            | TokenType::Alter
27397                            | TokenType::Drop
27398                            | TokenType::Set
27399                            | TokenType::System
27400                            | TokenType::Table
27401                    ) || matches!(
27402                        t.text.to_ascii_uppercase().as_str(),
27403                        "SYNTAX" | "AST" | "PLAN" | "PIPELINE" | "ESTIMATE" | "CURRENT" | "QUERY"
27404                    ) || (t.token_type == TokenType::Var
27405                        && self
27406                            .peek_nth(2)
27407                            .map_or(false, |t2| t2.token_type == TokenType::Eq))
27408                });
27409            // ClickHouse: (from, to, ...) -> body is a tuple-lambda with keyword params
27410            // Detect pattern: (keyword/ident, keyword/ident, ...) ->
27411            if matches!(
27412                self.config.dialect,
27413                Some(crate::dialects::DialectType::ClickHouse)
27414            ) {
27415                let mut look = self.current;
27416                let mut is_tuple_lambda = true;
27417                let mut param_count = 0;
27418                loop {
27419                    if look >= self.tokens.len() {
27420                        is_tuple_lambda = false;
27421                        break;
27422                    }
27423                    let tt = self.tokens[look].token_type;
27424                    if tt == TokenType::Identifier
27425                        || tt == TokenType::Var
27426                        || tt == TokenType::QuotedIdentifier
27427                        || tt.is_keyword()
27428                    {
27429                        param_count += 1;
27430                        look += 1;
27431                    } else {
27432                        is_tuple_lambda = false;
27433                        break;
27434                    }
27435                    if look >= self.tokens.len() {
27436                        is_tuple_lambda = false;
27437                        break;
27438                    }
27439                    if self.tokens[look].token_type == TokenType::Comma {
27440                        look += 1;
27441                    } else if self.tokens[look].token_type == TokenType::RParen {
27442                        look += 1;
27443                        break;
27444                    } else {
27445                        is_tuple_lambda = false;
27446                        break;
27447                    }
27448                }
27449                if is_tuple_lambda
27450                    && param_count >= 1
27451                    && look < self.tokens.len()
27452                    && self.tokens[look].token_type == TokenType::Arrow
27453                {
27454                    // Parse as lambda: consume params
27455                    let mut params = Vec::new();
27456                    loop {
27457                        let tok = self.advance();
27458                        params.push(Identifier::new(tok.text));
27459                        if self.match_token(TokenType::Comma) {
27460                            continue;
27461                        }
27462                        break;
27463                    }
27464                    self.expect(TokenType::RParen)?;
27465                    self.expect(TokenType::Arrow)?;
27466                    let body = self.parse_expression()?;
27467                    return Ok(Expression::Lambda(Box::new(LambdaExpr {
27468                        parameters: params,
27469                        body,
27470                        colon: false,
27471                        parameter_types: Vec::new(),
27472                    })));
27473                }
27474            }
27475            if self.check(TokenType::Select)
27476                || self.check(TokenType::With)
27477                || self.check(TokenType::From)
27478                || is_explain_subquery
27479            {
27480                let query = self.parse_statement()?;
27481
27482                // Parse LIMIT/OFFSET that may appear after set operations INSIDE the parentheses
27483                // e.g., (SELECT 1 EXCEPT (SELECT 2) LIMIT 1)
27484                let limit = if self.match_token(TokenType::Limit) {
27485                    Some(Limit {
27486                        this: self.parse_expression()?,
27487                        percent: false,
27488                        comments: Vec::new(),
27489                    })
27490                } else {
27491                    None
27492                };
27493                let offset = if self.match_token(TokenType::Offset) {
27494                    Some(Offset {
27495                        this: self.parse_expression()?,
27496                        rows: None,
27497                    })
27498                } else {
27499                    None
27500                };
27501
27502                self.expect(TokenType::RParen)?;
27503
27504                // Wrap in Subquery to preserve parentheses in set operations
27505                let subquery = if limit.is_some() || offset.is_some() {
27506                    // If we have limit/offset INSIDE the parens, set modifiers_inside = true
27507                    Expression::Subquery(Box::new(Subquery {
27508                        this: query,
27509                        alias: None,
27510                        column_aliases: Vec::new(),
27511                        order_by: None,
27512                        limit,
27513                        offset,
27514                        distribute_by: None,
27515                        sort_by: None,
27516                        cluster_by: None,
27517                        lateral: false,
27518                        modifiers_inside: true,
27519                        trailing_comments: self.previous_trailing_comments().to_vec(),
27520                        inferred_type: None,
27521                    }))
27522                } else {
27523                    Expression::Subquery(Box::new(Subquery {
27524                        this: query,
27525                        alias: None,
27526                        column_aliases: Vec::new(),
27527                        order_by: None,
27528                        limit: None,
27529                        offset: None,
27530                        distribute_by: None,
27531                        sort_by: None,
27532                        cluster_by: None,
27533                        lateral: false,
27534                        modifiers_inside: false,
27535                        trailing_comments: self.previous_trailing_comments().to_vec(),
27536                        inferred_type: None,
27537                    }))
27538                };
27539
27540                // Check for set operations after the subquery (e.g., (SELECT 1) UNION (SELECT 2))
27541                let set_result = self.parse_set_operation(subquery)?;
27542
27543                // Only parse ORDER BY/LIMIT/OFFSET after set operations if there WAS a set operation
27544                // (for cases like ((SELECT 0) UNION (SELECT 1) ORDER BY 1 OFFSET 1))
27545                // If there's no set operation, we should NOT consume these - they belong to outer context
27546                let had_set_operation = matches!(
27547                    &set_result,
27548                    Expression::Union(_) | Expression::Intersect(_) | Expression::Except(_)
27549                );
27550
27551                let result = if had_set_operation {
27552                    let order_by = if self.check(TokenType::Order) {
27553                        self.expect(TokenType::Order)?;
27554                        self.expect(TokenType::By)?;
27555                        Some(self.parse_order_by()?)
27556                    } else {
27557                        None
27558                    };
27559                    let limit_after = if self.match_token(TokenType::Limit) {
27560                        Some(Limit {
27561                            this: self.parse_expression()?,
27562                            percent: false,
27563                            comments: Vec::new(),
27564                        })
27565                    } else {
27566                        None
27567                    };
27568                    let offset_after = if self.match_token(TokenType::Offset) {
27569                        Some(Offset {
27570                            this: self.parse_expression()?,
27571                            rows: None,
27572                        })
27573                    } else {
27574                        None
27575                    };
27576
27577                    // If we have any modifiers, wrap in a Subquery with the modifiers OUTSIDE the paren
27578                    if order_by.is_some() || limit_after.is_some() || offset_after.is_some() {
27579                        Expression::Subquery(Box::new(Subquery {
27580                            this: set_result,
27581                            alias: None,
27582                            column_aliases: Vec::new(),
27583                            order_by,
27584                            limit: limit_after,
27585                            offset: offset_after,
27586                            lateral: false,
27587                            modifiers_inside: false,
27588                            trailing_comments: Vec::new(),
27589                            distribute_by: None,
27590                            sort_by: None,
27591                            cluster_by: None,
27592                            inferred_type: None,
27593                        }))
27594                    } else {
27595                        set_result
27596                    }
27597                } else {
27598                    set_result
27599                };
27600                // Allow postfix operators on subquery expressions (e.g., (SELECT 1, 2).1 for tuple element access)
27601                return self.maybe_parse_subscript(result);
27602            }
27603
27604            // Check if this starts with another paren that might be a subquery
27605            // e.g., ((SELECT 1))
27606            if self.check(TokenType::LParen) {
27607                let expr = self.parse_expression()?;
27608
27609                // Handle aliasing of expression inside outer parens (e.g., ((a, b) AS c))
27610                let first_expr = if self.match_token(TokenType::As) {
27611                    let alias = self.expect_identifier_or_alias_keyword_with_quoted()?;
27612                    Expression::Alias(Box::new(Alias::new(expr, alias)))
27613                } else {
27614                    expr
27615                };
27616
27617                // Check for tuple of tuples: ((1, 2), (3, 4))
27618                // Also handles ClickHouse: ((SELECT 1) AS x, (SELECT 2) AS y)
27619                if self.match_token(TokenType::Comma) {
27620                    let mut expressions = vec![first_expr];
27621                    loop {
27622                        if self.check(TokenType::RParen) {
27623                            break;
27624                        } // trailing comma
27625                        let elem = self.parse_expression()?;
27626                        // Handle AS alias after each element (ClickHouse tuple CTE pattern)
27627                        let elem = if self.match_token(TokenType::As) {
27628                            let alias = self.expect_identifier_or_keyword()?;
27629                            Expression::Alias(Box::new(Alias::new(elem, Identifier::new(alias))))
27630                        } else {
27631                            elem
27632                        };
27633                        expressions.push(elem);
27634                        if !self.match_token(TokenType::Comma) {
27635                            break;
27636                        }
27637                    }
27638                    self.expect(TokenType::RParen)?;
27639                    let tuple_expr = Expression::Tuple(Box::new(Tuple { expressions }));
27640                    return self.maybe_parse_subscript(tuple_expr);
27641                }
27642
27643                let result = first_expr;
27644
27645                self.expect(TokenType::RParen)?;
27646                let mut nested_paren_comments = lparen_comments.clone();
27647                nested_paren_comments.extend_from_slice(self.previous_trailing_comments());
27648                // Check for set operations after parenthesized expression
27649                if self.check(TokenType::Union)
27650                    || self.check(TokenType::Intersect)
27651                    || self.check(TokenType::Except)
27652                {
27653                    // This is a set operation - need to handle specially
27654                    if let Expression::Subquery(subq) = &result {
27655                        let set_result = self.parse_set_operation(subq.this.clone())?;
27656
27657                        // Parse ORDER BY/LIMIT/OFFSET after set operations
27658                        let order_by = if self.check(TokenType::Order) {
27659                            self.expect(TokenType::Order)?;
27660                            self.expect(TokenType::By)?;
27661                            Some(self.parse_order_by()?)
27662                        } else {
27663                            None
27664                        };
27665                        let limit = if self.match_token(TokenType::Limit) {
27666                            Some(Limit {
27667                                this: self.parse_expression()?,
27668                                percent: false,
27669                                comments: Vec::new(),
27670                            })
27671                        } else {
27672                            None
27673                        };
27674                        let offset = if self.match_token(TokenType::Offset) {
27675                            Some(Offset {
27676                                this: self.parse_expression()?,
27677                                rows: None,
27678                            })
27679                        } else {
27680                            None
27681                        };
27682
27683                        return Ok(Expression::Subquery(Box::new(Subquery {
27684                            this: set_result,
27685                            alias: None,
27686                            column_aliases: Vec::new(),
27687                            order_by,
27688                            limit,
27689                            offset,
27690                            lateral: false,
27691                            modifiers_inside: false,
27692                            trailing_comments: Vec::new(),
27693                            distribute_by: None,
27694                            sort_by: None,
27695                            cluster_by: None,
27696                            inferred_type: None,
27697                        })));
27698                    }
27699                }
27700                return self.maybe_parse_over(Expression::Paren(Box::new(Paren {
27701                    this: result,
27702                    trailing_comments: nested_paren_comments,
27703                })));
27704            }
27705
27706            let expr = self.parse_expression()?;
27707
27708            // Check for AS alias on the first element (e.g., (x AS y, ...))
27709            let first_expr = if self.match_token(TokenType::As) {
27710                let alias = self.expect_identifier_or_keyword_with_quoted()?;
27711                Expression::Alias(Box::new(Alias::new(expr, alias)))
27712            } else {
27713                expr
27714            };
27715
27716            // Check for tuple (multiple expressions separated by commas)
27717            if self.match_token(TokenType::Comma) {
27718                let mut expressions = vec![first_expr];
27719                // ClickHouse: trailing comma creates single-element tuple, e.g., (1,)
27720                if self.check(TokenType::RParen) {
27721                    self.skip(); // consume )
27722                    let tuple_expr = Expression::Tuple(Box::new(Tuple { expressions }));
27723                    return self.maybe_parse_subscript(tuple_expr);
27724                }
27725                // Parse remaining tuple elements, each can have AS alias
27726                loop {
27727                    let elem = self.parse_expression()?;
27728                    let elem_with_alias = if self.match_token(TokenType::As) {
27729                        let alias = self.expect_identifier_or_keyword_with_quoted()?;
27730                        Expression::Alias(Box::new(Alias::new(elem, alias)))
27731                    } else {
27732                        elem
27733                    };
27734                    expressions.push(elem_with_alias);
27735                    if !self.match_token(TokenType::Comma) {
27736                        break;
27737                    }
27738                    // ClickHouse: trailing comma in multi-element tuple, e.g., (1, 2,)
27739                    if self.check(TokenType::RParen) {
27740                        break;
27741                    }
27742                }
27743
27744                self.expect(TokenType::RParen)?;
27745
27746                // Check for lambda expression: (a, b) -> body
27747                if self.match_token(TokenType::Arrow) {
27748                    let parameters = expressions
27749                        .into_iter()
27750                        .filter_map(|e| {
27751                            if let Expression::Column(c) = e {
27752                                Some(c.name)
27753                            } else if let Expression::Identifier(id) = e {
27754                                Some(id)
27755                            } else {
27756                                None
27757                            }
27758                        })
27759                        .collect();
27760                    let body = self.parse_expression()?;
27761                    return Ok(Expression::Lambda(Box::new(LambdaExpr {
27762                        parameters,
27763                        body,
27764                        colon: false,
27765                        parameter_types: Vec::new(),
27766                    })));
27767                }
27768
27769                // Check for optional alias on the whole tuple
27770                // But NOT when AS is followed by a type constructor like Tuple(a Int8, ...)
27771                // or STRUCT<a TINYINT, ...> which would be part of a CAST expression: CAST((1, 2) AS Tuple(a Int8, b Int16))
27772                // Also NOT when AS is followed by a type name then ) like: CAST((1, 2) AS String)
27773                let tuple_expr = Expression::Tuple(Box::new(Tuple { expressions }));
27774                let result = if self.check(TokenType::As) {
27775                    // Look ahead: AS + type_keyword + ( or < → likely a type, not an alias
27776                    let after_as = self.current + 1;
27777                    let after_ident = self.current + 2;
27778                    let is_type_constructor = after_ident < self.tokens.len()
27779                        && (self.tokens[after_as].token_type == TokenType::Identifier
27780                            || self.tokens[after_as].token_type == TokenType::Var
27781                            || self.tokens[after_as].token_type == TokenType::Nullable
27782                            || self.tokens[after_as].token_type == TokenType::Struct
27783                            || self.tokens[after_as].token_type == TokenType::Array)
27784                        && (self.tokens[after_ident].token_type == TokenType::LParen
27785                            || self.tokens[after_ident].token_type == TokenType::Lt);
27786                    // Check if AS is followed by identifier/keyword then ), indicating CAST(tuple AS Type)
27787                    let is_cast_type = after_ident < self.tokens.len()
27788                        && (self.tokens[after_as].token_type == TokenType::Identifier
27789                            || self.tokens[after_as].token_type == TokenType::Var
27790                            || self.tokens[after_as].token_type.is_keyword())
27791                        && self.tokens[after_ident].token_type == TokenType::RParen;
27792                    if is_type_constructor || is_cast_type {
27793                        tuple_expr
27794                    } else {
27795                        self.skip(); // consume AS
27796                        let alias = self.expect_identifier()?;
27797                        Expression::Alias(Box::new(Alias::new(tuple_expr, Identifier::new(alias))))
27798                    }
27799                } else {
27800                    tuple_expr
27801                };
27802
27803                // Allow postfix operators on tuple expressions (e.g., ('a', 'b').1 for tuple element access)
27804                return self.maybe_parse_subscript(result);
27805            }
27806
27807            // ClickHouse: (x -> body) — lambda inside parentheses
27808            if matches!(
27809                self.config.dialect,
27810                Some(crate::dialects::DialectType::ClickHouse)
27811            ) && self.match_token(TokenType::Arrow)
27812            {
27813                let parameters = if let Expression::Column(c) = first_expr {
27814                    vec![c.name]
27815                } else if let Expression::Identifier(id) = first_expr {
27816                    vec![id]
27817                } else {
27818                    return Err(self.parse_error("Expected identifier as lambda parameter"));
27819                };
27820                let body = self.parse_expression()?;
27821                self.expect(TokenType::RParen)?;
27822                return Ok(Expression::Paren(Box::new(Paren {
27823                    this: Expression::Lambda(Box::new(LambdaExpr {
27824                        parameters,
27825                        body,
27826                        colon: false,
27827                        parameter_types: Vec::new(),
27828                    })),
27829                    trailing_comments: Vec::new(),
27830                })));
27831            }
27832
27833            self.expect(TokenType::RParen)?;
27834            // Combine comments from ( and ) tokens
27835            let mut paren_comments = lparen_comments.clone();
27836            paren_comments.extend_from_slice(self.previous_trailing_comments());
27837
27838            // Check for lambda expression: (x) -> body or single identifier case
27839            if self.match_token(TokenType::Arrow) {
27840                // first_expr should be a single identifier for the parameter
27841                let parameters = if let Expression::Column(c) = first_expr {
27842                    vec![c.name]
27843                } else if let Expression::Identifier(id) = first_expr {
27844                    vec![id]
27845                } else {
27846                    return Err(self.parse_error("Expected identifier as lambda parameter"));
27847                };
27848                let body = self.parse_expression()?;
27849                return Ok(Expression::Lambda(Box::new(LambdaExpr {
27850                    parameters,
27851                    body,
27852                    colon: false,
27853                    parameter_types: Vec::new(),
27854                })));
27855            }
27856
27857            return self.maybe_parse_over(Expression::Paren(Box::new(Paren {
27858                this: first_expr,
27859                trailing_comments: paren_comments,
27860            })));
27861        }
27862
27863        // NULL
27864        if self.match_token(TokenType::Null) {
27865            return Ok(Expression::Null(Null));
27866        }
27867
27868        // TRUE
27869        if self.match_token(TokenType::True) {
27870            return Ok(Expression::Boolean(BooleanLiteral { value: true }));
27871        }
27872
27873        // FALSE
27874        if self.match_token(TokenType::False) {
27875            return Ok(Expression::Boolean(BooleanLiteral { value: false }));
27876        }
27877
27878        // LAMBDA expression (DuckDB syntax: LAMBDA x : expr)
27879        if self.check(TokenType::Lambda) {
27880            if let Some(lambda) = self.parse_lambda()? {
27881                return Ok(lambda);
27882            }
27883        }
27884
27885        // CASE expression - but not if followed by DOT (then it's an identifier like case.column)
27886        if self.check(TokenType::Case) && !self.check_next(TokenType::Dot) {
27887            let case_expr = self.parse_case()?;
27888            return self.maybe_parse_over(case_expr);
27889        }
27890
27891        // CAST expression
27892        if self.check(TokenType::Cast) {
27893            let cast_expr = self.parse_cast()?;
27894            return self.maybe_parse_subscript(cast_expr);
27895        }
27896
27897        // TRY_CAST expression
27898        if self.check(TokenType::TryCast) {
27899            let cast_expr = self.parse_try_cast()?;
27900            return self.maybe_parse_subscript(cast_expr);
27901        }
27902
27903        // SAFE_CAST expression (BigQuery)
27904        if self.check(TokenType::SafeCast) {
27905            let cast_expr = self.parse_safe_cast()?;
27906            return self.maybe_parse_subscript(cast_expr);
27907        }
27908
27909        // EXISTS - either subquery predicate EXISTS(SELECT ...) or Hive array function EXISTS(array, lambda)
27910        // ClickHouse: EXISTS without ( is a column name/identifier
27911        if self.check(TokenType::Exists)
27912            && matches!(
27913                self.config.dialect,
27914                Some(crate::dialects::DialectType::ClickHouse)
27915            )
27916            && !self.check_next(TokenType::LParen)
27917        {
27918            let tok = self.advance();
27919            return Ok(Expression::Identifier(Identifier::new(tok.text)));
27920        }
27921        if self.match_token(TokenType::Exists) {
27922            self.expect(TokenType::LParen)?;
27923
27924            // Check if this is a subquery EXISTS (SELECT, WITH, or FROM for DuckDB)
27925            // ClickHouse: also handle EXISTS((SELECT ...)) with double parens
27926            if self.check(TokenType::Select)
27927                || self.check(TokenType::With)
27928                || self.check(TokenType::From)
27929                || (self.check(TokenType::LParen)
27930                    && self
27931                        .peek_nth(1)
27932                        .map(|t| {
27933                            matches!(
27934                                t.token_type,
27935                                TokenType::Select | TokenType::With | TokenType::From
27936                            )
27937                        })
27938                        .unwrap_or(false))
27939            {
27940                let query = self.parse_statement()?;
27941                self.expect(TokenType::RParen)?;
27942                return Ok(Expression::Exists(Box::new(Exists {
27943                    this: query,
27944                    not: false,
27945                })));
27946            }
27947
27948            // Otherwise it's Hive's array EXISTS function: EXISTS(array, lambda_predicate)
27949            // This function checks if any element in the array matches the predicate
27950            let array_expr = self.parse_expression()?;
27951            self.expect(TokenType::Comma)?;
27952            let predicate = self.parse_expression()?;
27953            self.expect(TokenType::RParen)?;
27954            return Ok(Expression::Function(Box::new(Function {
27955                name: "EXISTS".to_string(),
27956                args: vec![array_expr, predicate],
27957                distinct: false,
27958                trailing_comments: Vec::new(),
27959                use_bracket_syntax: false,
27960                no_parens: false,
27961                quoted: false,
27962                span: None,
27963                inferred_type: None,
27964            })));
27965        }
27966
27967        // INTERVAL expression or identifier
27968        if self.check(TokenType::Interval) {
27969            if let Some(interval_expr) = self.try_parse_interval()? {
27970                return Ok(interval_expr);
27971            }
27972            // INTERVAL is used as an identifier
27973            let token = self.advance();
27974            return Ok(Expression::Identifier(Identifier::new(token.text)));
27975        }
27976
27977        // DATE literal: DATE '2024-01-15' or DATE function: DATE(expr)
27978        if self.check(TokenType::Date) {
27979            let token = self.advance();
27980            let original_text = token.text.clone();
27981            if self.check(TokenType::String) {
27982                let str_token = self.advance();
27983                if self.config.dialect.is_none() {
27984                    // Generic (no dialect): DATE 'literal' -> CAST('literal' AS DATE)
27985                    return Ok(Expression::Cast(Box::new(Cast {
27986                        this: Expression::Literal(Literal::String(str_token.text)),
27987                        to: DataType::Date,
27988                        trailing_comments: Vec::new(),
27989                        double_colon_syntax: false,
27990                        format: None,
27991                        default: None,
27992                        inferred_type: None,
27993                    })));
27994                }
27995                return Ok(Expression::Literal(Literal::Date(str_token.text)));
27996            }
27997            // Check for DATE() function call
27998            if self.match_token(TokenType::LParen) {
27999                let func_expr = self.parse_typed_function(&original_text, "DATE", false)?;
28000                return self.maybe_parse_over(func_expr);
28001            }
28002            // Fallback to DATE as identifier/type - preserve original case
28003            return Ok(Expression::Identifier(Identifier::new(original_text)));
28004        }
28005
28006        // TIME literal: TIME '10:30:00' or TIME function: TIME(expr)
28007        if self.check(TokenType::Time) {
28008            let token = self.advance();
28009            let original_text = token.text.clone();
28010            if self.check(TokenType::String) {
28011                let str_token = self.advance();
28012                return Ok(Expression::Literal(Literal::Time(str_token.text)));
28013            }
28014            // Check for TIME() function call
28015            if self.match_token(TokenType::LParen) {
28016                let func_expr = self.parse_typed_function(&original_text, "TIME", false)?;
28017                return self.maybe_parse_over(func_expr);
28018            }
28019            // Fallback to TIME as identifier/type - preserve original case
28020            return self
28021                .maybe_parse_subscript(Expression::Identifier(Identifier::new(original_text)));
28022        }
28023
28024        // TIMESTAMP literal: TIMESTAMP '2024-01-15 10:30:00' or TIMESTAMP function: TIMESTAMP(expr)
28025        // Also handles TIMESTAMP(n) WITH TIME ZONE as a data type expression
28026        if self.check(TokenType::Timestamp) {
28027            let token = self.advance();
28028            let original_text = token.text.clone();
28029            if self.check(TokenType::String) {
28030                let str_token = self.advance();
28031                if self.config.dialect.is_none() {
28032                    // Generic (no dialect): TIMESTAMP 'literal' -> CAST('literal' AS TIMESTAMP)
28033                    return Ok(Expression::Cast(Box::new(Cast {
28034                        this: Expression::Literal(Literal::String(str_token.text)),
28035                        to: DataType::Timestamp {
28036                            precision: None,
28037                            timezone: false,
28038                        },
28039                        trailing_comments: Vec::new(),
28040                        double_colon_syntax: false,
28041                        format: None,
28042                        default: None,
28043                        inferred_type: None,
28044                    })));
28045                }
28046                // Dialect-specific: keep as Literal::Timestamp for dialect transforms
28047                return Ok(Expression::Literal(Literal::Timestamp(str_token.text)));
28048            }
28049            // Check for TIMESTAMP(n) WITH/WITHOUT TIME ZONE or TIMESTAMP(n) 'literal' as data type
28050            // This is a data type, not a function call
28051            if self.check(TokenType::LParen) {
28052                // Look ahead to see if this is TIMESTAMP(number) WITH/WITHOUT/String (data type)
28053                // vs TIMESTAMP(expr) (function call)
28054                let is_data_type = self.check_next(TokenType::Number) && {
28055                    // Check if after (number) there's WITH, WITHOUT, or String literal
28056                    let mut lookahead = self.current + 2;
28057                    // Skip the number
28058                    while lookahead < self.tokens.len()
28059                        && self.tokens[lookahead].token_type == TokenType::RParen
28060                    {
28061                        lookahead += 1;
28062                        break;
28063                    }
28064                    // Check for WITH, WITHOUT, or String after the closing paren
28065                    lookahead < self.tokens.len()
28066                        && (self.tokens[lookahead].token_type == TokenType::With
28067                            || self.tokens[lookahead].text.eq_ignore_ascii_case("WITHOUT")
28068                            || self.tokens[lookahead].token_type == TokenType::String)
28069                };
28070
28071                if is_data_type {
28072                    // Parse as data type: TIMESTAMP(precision) [WITH/WITHOUT TIME ZONE] ['literal']
28073                    self.skip(); // consume (
28074                    let precision = Some(self.expect_number()? as u32);
28075                    self.expect(TokenType::RParen)?;
28076
28077                    let data_type = if self.match_token(TokenType::With) {
28078                        if self.match_token(TokenType::Local) {
28079                            // WITH LOCAL TIME ZONE -> TIMESTAMPLTZ
28080                            self.match_keyword("TIME");
28081                            self.match_keyword("ZONE");
28082                            DataType::Custom {
28083                                name: format!("TIMESTAMPLTZ({})", precision.unwrap()),
28084                            }
28085                        } else {
28086                            self.match_keyword("TIME");
28087                            self.match_keyword("ZONE");
28088                            DataType::Timestamp {
28089                                precision,
28090                                timezone: true,
28091                            }
28092                        }
28093                    } else if self.match_keyword("WITHOUT") {
28094                        self.match_keyword("TIME");
28095                        self.match_keyword("ZONE");
28096                        DataType::Timestamp {
28097                            precision,
28098                            timezone: false,
28099                        }
28100                    } else {
28101                        DataType::Timestamp {
28102                            precision,
28103                            timezone: false,
28104                        }
28105                    };
28106
28107                    // Check for following string literal -> wrap in CAST
28108                    if self.check(TokenType::String) {
28109                        let str_token = self.advance();
28110                        return Ok(Expression::Cast(Box::new(Cast {
28111                            this: Expression::Literal(Literal::String(str_token.text)),
28112                            to: data_type,
28113                            trailing_comments: Vec::new(),
28114                            double_colon_syntax: false,
28115                            format: None,
28116                            default: None,
28117                            inferred_type: None,
28118                        })));
28119                    }
28120
28121                    return Ok(Expression::DataType(data_type));
28122                }
28123
28124                // Otherwise parse as function call
28125                self.skip(); // consume (
28126                let func_expr = self.parse_typed_function(&original_text, "TIMESTAMP", false)?;
28127                return self.maybe_parse_over(func_expr);
28128            }
28129            // Check for TIMESTAMP WITH/WITHOUT TIME ZONE (no precision) as data type
28130            // Use lookahead to verify WITH is followed by TIME (not WITH FILL, WITH TOTALS, etc.)
28131            if (self.check(TokenType::With)
28132                && self.peek_nth(1).map_or(false, |t| {
28133                    t.text.eq_ignore_ascii_case("TIME") || t.text.eq_ignore_ascii_case("LOCAL")
28134                }))
28135                || self.check_keyword_text("WITHOUT")
28136            {
28137                let data_type = if self.match_token(TokenType::With) {
28138                    if self.match_token(TokenType::Local) {
28139                        // WITH LOCAL TIME ZONE -> TIMESTAMPLTZ
28140                        self.match_keyword("TIME");
28141                        self.match_keyword("ZONE");
28142                        DataType::Custom {
28143                            name: "TIMESTAMPLTZ".to_string(),
28144                        }
28145                    } else {
28146                        self.match_keyword("TIME");
28147                        self.match_keyword("ZONE");
28148                        DataType::Timestamp {
28149                            precision: None,
28150                            timezone: true,
28151                        }
28152                    }
28153                } else if self.match_keyword("WITHOUT") {
28154                    self.match_keyword("TIME");
28155                    self.match_keyword("ZONE");
28156                    DataType::Timestamp {
28157                        precision: None,
28158                        timezone: false,
28159                    }
28160                } else {
28161                    DataType::Timestamp {
28162                        precision: None,
28163                        timezone: false,
28164                    }
28165                };
28166
28167                // Check for following string literal -> wrap in CAST
28168                if self.check(TokenType::String) {
28169                    let str_token = self.advance();
28170                    return Ok(Expression::Cast(Box::new(Cast {
28171                        this: Expression::Literal(Literal::String(str_token.text)),
28172                        to: data_type,
28173                        trailing_comments: Vec::new(),
28174                        double_colon_syntax: false,
28175                        format: None,
28176                        default: None,
28177                        inferred_type: None,
28178                    })));
28179                }
28180
28181                return Ok(Expression::DataType(data_type));
28182            }
28183            // Fallback to TIMESTAMP as identifier/type - preserve original case
28184            return Ok(Expression::Identifier(Identifier::new(original_text)));
28185        }
28186
28187        // DATETIME literal: DATETIME '2024-01-15 10:30:00' or DATETIME function: DATETIME(expr)
28188        if self.check(TokenType::DateTime) {
28189            let token = self.advance();
28190            let original_text = token.text.clone();
28191            if self.check(TokenType::String) {
28192                let str_token = self.advance();
28193                return Ok(Expression::Literal(Literal::Datetime(str_token.text)));
28194            }
28195            // Check for DATETIME() function call
28196            if self.match_token(TokenType::LParen) {
28197                let func_expr = self.parse_typed_function(&original_text, "DATETIME", false)?;
28198                return self.maybe_parse_over(func_expr);
28199            }
28200            // Fallback to DATETIME as identifier/type - preserve original case
28201            return Ok(Expression::Identifier(Identifier::new(original_text)));
28202        }
28203
28204        // ROW() function (window function for row number)
28205        if self.check(TokenType::Row) && self.check_next(TokenType::LParen) {
28206            self.skip(); // consume ROW
28207            self.expect(TokenType::LParen)?;
28208            // ROW() typically takes no arguments
28209            let args = if !self.check(TokenType::RParen) {
28210                self.parse_expression_list()?
28211            } else {
28212                Vec::new()
28213            };
28214            self.expect(TokenType::RParen)?;
28215            let func_expr = Expression::Function(Box::new(Function {
28216                name: "ROW".to_string(),
28217                args,
28218                distinct: false,
28219                trailing_comments: Vec::new(),
28220                use_bracket_syntax: false,
28221                no_parens: false,
28222                quoted: false,
28223                span: None,
28224                inferred_type: None,
28225            }));
28226            return self.maybe_parse_over(func_expr);
28227        }
28228
28229        // Number - support postfix operators like ::type
28230        if self.check(TokenType::Number) {
28231            let token = self.advance();
28232            if matches!(
28233                self.config.dialect,
28234                Some(crate::dialects::DialectType::MySQL)
28235            ) {
28236                let text = token.text.as_str();
28237                if text.len() > 2
28238                    && (text.starts_with("0x") || text.starts_with("0X"))
28239                    && !text[2..].chars().all(|c| c.is_ascii_hexdigit())
28240                {
28241                    let ident = Expression::Identifier(Identifier {
28242                        name: token.text,
28243                        quoted: true,
28244                        trailing_comments: Vec::new(),
28245                        span: None,
28246                    });
28247                    return self.maybe_parse_subscript(ident);
28248                }
28249            }
28250            if matches!(
28251                self.config.dialect,
28252                Some(crate::dialects::DialectType::Teradata)
28253            ) && token.text == "0"
28254            {
28255                if let Some(next) = self.tokens.get(self.current) {
28256                    let is_adjacent = token.span.end == next.span.start;
28257                    let next_text = next.text.as_str();
28258                    let is_hex_prefix = next_text.starts_with('x') || next_text.starts_with('X');
28259                    if is_adjacent
28260                        && matches!(next.token_type, TokenType::Identifier | TokenType::Var)
28261                        && is_hex_prefix
28262                        && next_text.len() > 1
28263                        && next_text[1..].chars().all(|c| c.is_ascii_hexdigit())
28264                    {
28265                        // Consume the hex suffix token and emit a HexString literal
28266                        let hex_token = self.advance();
28267                        let hex = hex_token.text[1..].to_string();
28268                        let literal = Expression::Literal(Literal::HexString(hex));
28269                        return self.maybe_parse_subscript(literal);
28270                    }
28271                }
28272            }
28273            if matches!(
28274                self.config.dialect,
28275                Some(crate::dialects::DialectType::ClickHouse)
28276            ) {
28277                if let Some(next) = self.tokens.get(self.current) {
28278                    let is_adjacent = token.span.end == next.span.start;
28279                    if is_adjacent
28280                        && matches!(next.token_type, TokenType::Identifier | TokenType::Var)
28281                        && next.text.starts_with('_')
28282                    {
28283                        let suffix = next.text.clone();
28284                        self.skip(); // consume suffix token
28285                        let combined = format!("{}{}", token.text, suffix);
28286                        let literal = Expression::Literal(Literal::Number(combined));
28287                        return self.maybe_parse_subscript(literal);
28288                    }
28289                }
28290            }
28291            // Check for numeric literal suffix encoded as "number::TYPE" by tokenizer
28292            let literal = if let Some(sep_pos) = token.text.find("::") {
28293                let num_part = &token.text[..sep_pos];
28294                let type_name = &token.text[sep_pos + 2..];
28295                let num_expr = Expression::Literal(Literal::Number(num_part.to_string()));
28296                let data_type = match type_name {
28297                    "BIGINT" => crate::expressions::DataType::BigInt { length: None },
28298                    "SMALLINT" => crate::expressions::DataType::SmallInt { length: None },
28299                    "TINYINT" => crate::expressions::DataType::TinyInt { length: None },
28300                    "DOUBLE" => crate::expressions::DataType::Double {
28301                        precision: None,
28302                        scale: None,
28303                    },
28304                    "FLOAT" => crate::expressions::DataType::Float {
28305                        precision: None,
28306                        scale: None,
28307                        real_spelling: false,
28308                    },
28309                    "DECIMAL" => crate::expressions::DataType::Decimal {
28310                        precision: None,
28311                        scale: None,
28312                    },
28313                    _ => crate::expressions::DataType::Custom {
28314                        name: type_name.to_string(),
28315                    },
28316                };
28317                Expression::Cast(Box::new(crate::expressions::Cast {
28318                    this: num_expr,
28319                    to: data_type,
28320                    trailing_comments: Vec::new(),
28321                    double_colon_syntax: false,
28322                    format: None,
28323                    default: None,
28324                    inferred_type: None,
28325                }))
28326            } else {
28327                Expression::Literal(Literal::Number(token.text))
28328            };
28329            return self.maybe_parse_subscript(literal);
28330        }
28331
28332        // String - support postfix operators like ::type, ->, ->>
28333        // Also handle adjacent string literals (SQL standard) which concatenate: 'x' 'y' 'z' -> CONCAT('x', 'y', 'z')
28334        if self.check(TokenType::String) {
28335            let token = self.advance();
28336            let first_literal = Expression::Literal(Literal::String(token.text));
28337
28338            // Check for adjacent string literals (PostgreSQL and SQL standard feature)
28339            // 'x' 'y' 'z' should be treated as string concatenation
28340            if self.check(TokenType::String) {
28341                let mut expressions = vec![first_literal];
28342                while self.check(TokenType::String) {
28343                    let next_token = self.advance();
28344                    expressions.push(Expression::Literal(Literal::String(next_token.text)));
28345                }
28346                // Create CONCAT function call with all adjacent strings
28347                let concat_func =
28348                    Expression::Function(Box::new(Function::new("CONCAT", expressions)));
28349                return self.maybe_parse_subscript(concat_func);
28350            }
28351
28352            return self.maybe_parse_subscript(first_literal);
28353        }
28354
28355        // Dollar-quoted string: $$...$$ or $tag$...$tag$ -- preserve as DollarString
28356        // so the generator can handle dialect-specific conversion
28357        if self.check(TokenType::DollarString) {
28358            let token = self.advance();
28359            let literal = Expression::Literal(Literal::DollarString(token.text));
28360            return self.maybe_parse_subscript(literal);
28361        }
28362
28363        // Triple-quoted string with double quotes: """..."""
28364        if self.check(TokenType::TripleDoubleQuotedString) {
28365            let token = self.advance();
28366            let literal = Expression::Literal(Literal::TripleQuotedString(token.text, '"'));
28367            return self.maybe_parse_subscript(literal);
28368        }
28369
28370        // Triple-quoted string with single quotes: '''...'''
28371        if self.check(TokenType::TripleSingleQuotedString) {
28372            let token = self.advance();
28373            let literal = Expression::Literal(Literal::TripleQuotedString(token.text, '\''));
28374            return self.maybe_parse_subscript(literal);
28375        }
28376
28377        // National String (N'...')
28378        if self.check(TokenType::NationalString) {
28379            let token = self.advance();
28380            let literal = Expression::Literal(Literal::NationalString(token.text));
28381            return self.maybe_parse_subscript(literal);
28382        }
28383
28384        // Hex String (X'...')
28385        if self.check(TokenType::HexString) {
28386            let token = self.advance();
28387            let literal = Expression::Literal(Literal::HexString(token.text));
28388            return self.maybe_parse_subscript(literal);
28389        }
28390
28391        // Hex Number (0xA from BigQuery/SQLite) - integer in hex notation
28392        if self.check(TokenType::HexNumber) {
28393            let token = self.advance();
28394            if matches!(
28395                self.config.dialect,
28396                Some(crate::dialects::DialectType::MySQL)
28397            ) {
28398                let text = token.text.as_str();
28399                if text.len() > 2
28400                    && (text.starts_with("0x") || text.starts_with("0X"))
28401                    && !text[2..].chars().all(|c| c.is_ascii_hexdigit())
28402                {
28403                    let ident = Expression::Identifier(Identifier {
28404                        name: token.text,
28405                        quoted: true,
28406                        trailing_comments: Vec::new(),
28407                        span: None,
28408                    });
28409                    return self.maybe_parse_subscript(ident);
28410                }
28411            }
28412            let literal = Expression::Literal(Literal::HexNumber(token.text));
28413            return self.maybe_parse_subscript(literal);
28414        }
28415
28416        // Bit String (B'...')
28417        if self.check(TokenType::BitString) {
28418            let token = self.advance();
28419            let literal = Expression::Literal(Literal::BitString(token.text));
28420            return self.maybe_parse_subscript(literal);
28421        }
28422
28423        // Byte String (b"..." - BigQuery style)
28424        if self.check(TokenType::ByteString) {
28425            let token = self.advance();
28426            let literal = Expression::Literal(Literal::ByteString(token.text));
28427            return self.maybe_parse_subscript(literal);
28428        }
28429
28430        // Raw String (r"..." - BigQuery style, backslashes are literal)
28431        if self.check(TokenType::RawString) {
28432            let token = self.advance();
28433            // Raw strings preserve backslashes as literal characters.
28434            // The generator will handle escaping when converting to a regular string.
28435            let literal = Expression::Literal(Literal::RawString(token.text));
28436            return self.maybe_parse_subscript(literal);
28437        }
28438
28439        // Escape String (E'...' - PostgreSQL)
28440        if self.check(TokenType::EscapeString) {
28441            let token = self.advance();
28442            // EscapeString is stored as "E'content'" - extract just the content
28443            let literal = Expression::Literal(Literal::EscapeString(token.text));
28444            return self.maybe_parse_subscript(literal);
28445        }
28446
28447        // Star - check for DuckDB *COLUMNS(...) syntax first
28448        if self.check(TokenType::Star) {
28449            // DuckDB *COLUMNS(...) syntax: *COLUMNS(*), *COLUMNS('regex'), *COLUMNS(['col1', 'col2'])
28450            // Check if * is followed by COLUMNS and (
28451            if self.check_next_identifier("COLUMNS") {
28452                // Check if there's a ( after COLUMNS
28453                if self
28454                    .tokens
28455                    .get(self.current + 2)
28456                    .map(|t| t.token_type == TokenType::LParen)
28457                    .unwrap_or(false)
28458                {
28459                    self.skip(); // consume *
28460                    self.skip(); // consume COLUMNS
28461                    self.skip(); // consume (
28462
28463                    // Parse the argument: can be *, a regex string, or an array of column names
28464                    let arg = if self.check(TokenType::Star) {
28465                        self.skip(); // consume *
28466                        Expression::Star(Star {
28467                            table: None,
28468                            except: None,
28469                            replace: None,
28470                            rename: None,
28471                            trailing_comments: Vec::new(),
28472                            span: None,
28473                        })
28474                    } else {
28475                        self.parse_expression()?
28476                    };
28477
28478                    self.expect(TokenType::RParen)?;
28479
28480                    // Create Columns expression with unpack=true
28481                    return Ok(Expression::Columns(Box::new(Columns {
28482                        this: Box::new(arg),
28483                        unpack: Some(Box::new(Expression::Boolean(BooleanLiteral {
28484                            value: true,
28485                        }))),
28486                    })));
28487                }
28488            }
28489
28490            // Regular star
28491            self.skip(); // consume *
28492            let star = self.parse_star_modifiers(None)?;
28493            return Ok(Expression::Star(star));
28494        }
28495
28496        // Generic type expressions: ARRAY<T>, MAP<K,V>, STRUCT<...>
28497        // These are standalone type expressions (not in CAST context)
28498        // But also handle STRUCT<TYPE>(args) which becomes CAST(STRUCT(args) AS STRUCT<TYPE>)
28499        if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
28500            let name_upper = self.peek().text.to_ascii_uppercase();
28501            if (name_upper == "ARRAY" || name_upper == "MAP" || name_upper == "STRUCT")
28502                && self.check_next(TokenType::Lt)
28503            {
28504                self.skip(); // consume ARRAY/MAP/STRUCT
28505                let data_type = self.parse_data_type_from_name(&name_upper)?;
28506
28507                // Check for typed constructor: STRUCT<TYPE>(args) or ARRAY<TYPE>(args)
28508                // These become CAST(STRUCT(args) AS TYPE) or CAST(ARRAY(args) AS TYPE)
28509                if self.match_token(TokenType::LParen) {
28510                    if name_upper == "STRUCT" {
28511                        // Parse struct constructor arguments
28512                        let args = if self.check(TokenType::RParen) {
28513                            Vec::new()
28514                        } else {
28515                            self.parse_struct_args()?
28516                        };
28517                        self.expect(TokenType::RParen)?;
28518
28519                        // Convert args to Struct fields (all unnamed)
28520                        let fields: Vec<(Option<String>, Expression)> =
28521                            args.into_iter().map(|e| (None, e)).collect();
28522
28523                        // Create CAST(STRUCT(args) AS STRUCT<TYPE>)
28524                        let struct_expr = Expression::Struct(Box::new(Struct { fields }));
28525                        let cast_expr = Expression::Cast(Box::new(Cast {
28526                            this: struct_expr,
28527                            to: data_type,
28528                            trailing_comments: Vec::new(),
28529                            double_colon_syntax: false,
28530                            format: None,
28531                            default: None,
28532                            inferred_type: None,
28533                        }));
28534                        return self.maybe_parse_subscript(cast_expr);
28535                    } else if name_upper == "ARRAY" {
28536                        // Parse array constructor arguments
28537                        let mut expressions = Vec::new();
28538                        if !self.check(TokenType::RParen) {
28539                            loop {
28540                                expressions.push(self.parse_expression()?);
28541                                if !self.match_token(TokenType::Comma) {
28542                                    break;
28543                                }
28544                            }
28545                        }
28546                        self.expect(TokenType::RParen)?;
28547
28548                        // Create CAST(ARRAY[args] AS ARRAY<TYPE>)
28549                        let array_expr = Expression::Array(Box::new(Array { expressions }));
28550                        let cast_expr = Expression::Cast(Box::new(Cast {
28551                            this: array_expr,
28552                            to: data_type,
28553                            trailing_comments: Vec::new(),
28554                            double_colon_syntax: false,
28555                            format: None,
28556                            default: None,
28557                            inferred_type: None,
28558                        }));
28559                        return self.maybe_parse_subscript(cast_expr);
28560                    }
28561                } else if self.match_token(TokenType::LBracket) {
28562                    // ARRAY<TYPE>[values] or ARRAY<TYPE>[] - bracket-style array constructor
28563                    let expressions = if self.check(TokenType::RBracket) {
28564                        Vec::new()
28565                    } else {
28566                        self.parse_expression_list()?
28567                    };
28568                    self.expect(TokenType::RBracket)?;
28569                    // Create CAST(Array(values) AS DataType)
28570                    let array_expr = Expression::Array(Box::new(Array { expressions }));
28571                    let cast_expr = Expression::Cast(Box::new(Cast {
28572                        this: array_expr,
28573                        to: data_type,
28574                        trailing_comments: Vec::new(),
28575                        double_colon_syntax: false,
28576                        format: None,
28577                        default: None,
28578                        inferred_type: None,
28579                    }));
28580                    return self.maybe_parse_subscript(cast_expr);
28581                }
28582
28583                return Ok(Expression::DataType(data_type));
28584            }
28585            // DuckDB-style MAP with curly brace literals: MAP {'key': value}
28586            if name_upper == "MAP" && self.check_next(TokenType::LBrace) {
28587                self.skip(); // consume MAP
28588                self.expect(TokenType::LBrace)?;
28589
28590                // Handle empty: MAP {}
28591                if self.match_token(TokenType::RBrace) {
28592                    return self.maybe_parse_subscript(Expression::MapFunc(Box::new(
28593                        MapConstructor {
28594                            keys: Vec::new(),
28595                            values: Vec::new(),
28596                            curly_brace_syntax: true,
28597                            with_map_keyword: true,
28598                        },
28599                    )));
28600                }
28601
28602                // Parse key-value pairs
28603                let mut keys = Vec::new();
28604                let mut values = Vec::new();
28605                loop {
28606                    let key = self.parse_primary()?;
28607                    self.expect(TokenType::Colon)?;
28608                    let value = self.parse_expression()?;
28609                    keys.push(key);
28610                    values.push(value);
28611                    if !self.match_token(TokenType::Comma) {
28612                        break;
28613                    }
28614                    // Handle trailing comma
28615                    if self.check(TokenType::RBrace) {
28616                        break;
28617                    }
28618                }
28619                self.expect(TokenType::RBrace)?;
28620
28621                return self.maybe_parse_subscript(Expression::MapFunc(Box::new(MapConstructor {
28622                    keys,
28623                    values,
28624                    curly_brace_syntax: true,
28625                    with_map_keyword: true,
28626                })));
28627            }
28628        }
28629
28630        // Keywords as identifiers when followed by DOT (e.g., case.x, top.y)
28631        // These keywords can be table/column names when used with dot notation
28632        if (self.check(TokenType::Case) || self.check(TokenType::Top))
28633            && self.check_next(TokenType::Dot)
28634        {
28635            let token = self.advance();
28636            let ident = Identifier::new(token.text);
28637            self.expect(TokenType::Dot)?;
28638            if self.match_token(TokenType::Star) {
28639                // case.* or top.*
28640                let star = self.parse_star_modifiers(Some(ident))?;
28641                return Ok(Expression::Star(star));
28642            }
28643            // case.column or top.column
28644            let col_ident = self.expect_identifier_or_keyword_with_quoted()?;
28645            // Capture trailing comments from the column name token
28646            let trailing_comments = self.previous_trailing_comments().to_vec();
28647            let mut col = Expression::boxed_column(Column {
28648                name: col_ident,
28649                table: Some(ident),
28650                join_mark: false,
28651                trailing_comments,
28652                span: None,
28653                inferred_type: None,
28654            });
28655            // Handle Oracle/Redshift outer join marker (+) after column reference
28656            if self.check(TokenType::LParen) && self.check_next(TokenType::Plus) {
28657                let saved_pos = self.current;
28658                if self.match_token(TokenType::LParen)
28659                    && self.match_token(TokenType::Plus)
28660                    && self.match_token(TokenType::RParen)
28661                {
28662                    if let Expression::Column(ref mut c) = col {
28663                        c.join_mark = true;
28664                    }
28665                } else {
28666                    self.current = saved_pos;
28667                }
28668            }
28669            return self.maybe_parse_subscript(col);
28670        }
28671
28672        // MySQL BINARY prefix operator: BINARY expr -> CAST(expr AS BINARY)
28673        // Only treat as prefix operator when followed by an expression (not ( which would be BINARY() function,
28674        // and not when it would be a data type like BINARY in column definitions)
28675        if self.check(TokenType::Var)
28676            && self.peek().text.eq_ignore_ascii_case("BINARY")
28677            && !self.check_next(TokenType::LParen)
28678            && !self.check_next(TokenType::Dot)
28679            && !self.check_next(TokenType::RParen)
28680            && !self.check_next(TokenType::Comma)
28681            && !self.is_at_end()
28682        {
28683            // Check if this is actually followed by an expression token (not end of statement)
28684            let next_idx = self.current + 1;
28685            let has_expr = next_idx < self.tokens.len()
28686                && !matches!(
28687                    self.tokens[next_idx].token_type,
28688                    TokenType::Semicolon | TokenType::Eof | TokenType::RParen | TokenType::Comma
28689                );
28690            if has_expr {
28691                self.skip(); // consume BINARY
28692                let expr = self.parse_unary()?;
28693                return Ok(Expression::Cast(Box::new(Cast {
28694                    this: expr,
28695                    to: DataType::Binary { length: None },
28696                    trailing_comments: Vec::new(),
28697                    double_colon_syntax: false,
28698                    format: None,
28699                    default: None,
28700                    inferred_type: None,
28701                })));
28702            }
28703        }
28704
28705        // RLIKE/REGEXP as function call: RLIKE(expr, pattern, flags)
28706        // Normally RLIKE is an operator, but Snowflake allows function syntax
28707        if self.check(TokenType::RLike) && self.check_next(TokenType::LParen) {
28708            let token = self.advance(); // consume RLIKE
28709            self.skip(); // consume LParen
28710            let args = if self.check(TokenType::RParen) {
28711                Vec::new()
28712            } else {
28713                self.parse_function_arguments()?
28714            };
28715            self.expect(TokenType::RParen)?;
28716            let func = Expression::Function(Box::new(Function {
28717                name: token.text.clone(), // Preserve original case; generator handles normalization
28718                args,
28719                distinct: false,
28720                trailing_comments: Vec::new(),
28721                use_bracket_syntax: false,
28722                no_parens: false,
28723                quoted: false,
28724                span: None,
28725                inferred_type: None,
28726            }));
28727            return self.maybe_parse_over(func);
28728        }
28729
28730        // INSERT as function call: INSERT(str, pos, len, newstr)
28731        // Snowflake/MySQL have INSERT as a string function, but INSERT is also a DML keyword.
28732        // When followed by ( in expression context, treat as function call.
28733        if self.check(TokenType::Insert) && self.check_next(TokenType::LParen) {
28734            let token = self.advance(); // consume INSERT
28735            self.skip(); // consume LParen
28736            let args = if self.check(TokenType::RParen) {
28737                Vec::new()
28738            } else {
28739                self.parse_function_arguments()?
28740            };
28741            self.expect(TokenType::RParen)?;
28742            let func = Expression::Function(Box::new(Function {
28743                name: token.text.clone(),
28744                args,
28745                distinct: false,
28746                trailing_comments: Vec::new(),
28747                use_bracket_syntax: false,
28748                no_parens: false,
28749                quoted: false,
28750                span: None,
28751                inferred_type: None,
28752            }));
28753            return self.maybe_parse_over(func);
28754        }
28755
28756        // ClickHouse: MINUS/EXCEPT/INTERSECT/REGEXP as function names (e.g., minus(a, b), REGEXP('^db'))
28757        // MINUS is tokenized as TokenType::Except (Oracle alias), REGEXP as TokenType::RLike
28758        if matches!(
28759            self.config.dialect,
28760            Some(crate::dialects::DialectType::ClickHouse)
28761        ) && (self.check(TokenType::Except)
28762            || self.check(TokenType::Intersect)
28763            || self.check(TokenType::RLike))
28764            && self.check_next(TokenType::LParen)
28765        {
28766            let token = self.advance(); // consume keyword
28767            self.skip(); // consume LParen
28768            let args = if self.check(TokenType::RParen) {
28769                Vec::new()
28770            } else {
28771                self.parse_function_arguments()?
28772            };
28773            self.expect(TokenType::RParen)?;
28774            let func = Expression::Function(Box::new(Function {
28775                name: token.text.clone(),
28776                args,
28777                distinct: false,
28778                trailing_comments: Vec::new(),
28779                use_bracket_syntax: false,
28780                no_parens: false,
28781                quoted: false,
28782                span: None,
28783                inferred_type: None,
28784            }));
28785            return self.maybe_parse_over(func);
28786        }
28787
28788        // Handle CURRENT_DATE/CURRENT_TIMESTAMP/CURRENT_TIME/CURRENT_DATETIME with parentheses
28789        // These have special token types but BigQuery and others use them as function calls with args
28790        if matches!(
28791            self.peek().token_type,
28792            TokenType::CurrentDate
28793                | TokenType::CurrentTimestamp
28794                | TokenType::CurrentTime
28795                | TokenType::CurrentDateTime
28796        ) {
28797            // Snowflake: CURRENT_TIME / CURRENT_TIME(n) -> Localtime (so DuckDB can output LOCALTIME)
28798            if matches!(
28799                self.config.dialect,
28800                Some(crate::dialects::DialectType::Snowflake)
28801            ) && self.peek().token_type == TokenType::CurrentTime
28802            {
28803                self.skip(); // consume CURRENT_TIME
28804                if self.match_token(TokenType::LParen) {
28805                    // CURRENT_TIME(n) - consume args but ignore precision
28806                    if !self.check(TokenType::RParen) {
28807                        let _ = self.parse_function_arguments()?;
28808                    }
28809                    self.expect(TokenType::RParen)?;
28810                }
28811                return self.maybe_parse_subscript(Expression::Localtime(Box::new(
28812                    crate::expressions::Localtime { this: None },
28813                )));
28814            }
28815            if self.check_next(TokenType::LParen) {
28816                // Parse as function call: CURRENT_DATE('UTC'), CURRENT_TIMESTAMP(), etc.
28817                let token = self.advance(); // consume CURRENT_DATE etc.
28818                self.skip(); // consume LParen
28819                let args = if self.check(TokenType::RParen) {
28820                    Vec::new()
28821                } else {
28822                    self.parse_function_arguments()?
28823                };
28824                self.expect(TokenType::RParen)?;
28825                let func = Expression::Function(Box::new(Function {
28826                    name: token.text.clone(),
28827                    args,
28828                    distinct: false,
28829                    trailing_comments: Vec::new(),
28830                    use_bracket_syntax: false,
28831                    no_parens: false,
28832                    quoted: false,
28833                    span: None,
28834                    inferred_type: None,
28835                }));
28836                return self.maybe_parse_subscript(func);
28837            } else {
28838                // No parens - parse as no-paren function
28839                let token = self.advance();
28840                let func = Expression::Function(Box::new(Function {
28841                    name: token.text.clone(),
28842                    args: Vec::new(),
28843                    distinct: false,
28844                    trailing_comments: Vec::new(),
28845                    use_bracket_syntax: false,
28846                    no_parens: true,
28847                    quoted: false,
28848                    span: None,
28849                    inferred_type: None,
28850                }));
28851                return self.maybe_parse_subscript(func);
28852            }
28853        }
28854
28855        // Type keyword followed by string literal -> CAST('value' AS TYPE)
28856        // E.g., NUMERIC '2.25' -> CAST('2.25' AS NUMERIC)
28857        if self.is_identifier_token() && self.check_next(TokenType::String) {
28858            let upper_name = self.peek().text.to_ascii_uppercase();
28859            if matches!(
28860                upper_name.as_str(),
28861                "NUMERIC" | "DECIMAL" | "BIGNUMERIC" | "BIGDECIMAL"
28862            ) {
28863                self.skip(); // consume the type keyword
28864                let str_token = self.advance(); // consume the string literal
28865                let data_type = match upper_name.as_str() {
28866                    "NUMERIC" | "DECIMAL" | "BIGNUMERIC" | "BIGDECIMAL" => {
28867                        crate::expressions::DataType::Decimal {
28868                            precision: None,
28869                            scale: None,
28870                        }
28871                    }
28872                    _ => unreachable!("type keyword already matched in outer if-condition"),
28873                };
28874                return Ok(Expression::Cast(Box::new(crate::expressions::Cast {
28875                    this: Expression::Literal(Literal::String(str_token.text)),
28876                    to: data_type,
28877                    trailing_comments: Vec::new(),
28878                    double_colon_syntax: false,
28879                    format: None,
28880                    default: None,
28881                    inferred_type: None,
28882                })));
28883            }
28884        }
28885
28886        // Identifier, Column, or Function
28887        if self.is_identifier_token() {
28888            // Check for no-paren functions like CURRENT_TIMESTAMP, CURRENT_DATE, etc.
28889            // These should be parsed as functions even without parentheses
28890            let upper_name = self.peek().text.to_ascii_uppercase();
28891            if !self.check_next(TokenType::LParen)
28892                && !self.check_next(TokenType::Dot)
28893                && crate::function_registry::is_no_paren_function_name_upper(upper_name.as_str())
28894                && !(matches!(
28895                    self.config.dialect,
28896                    Some(crate::dialects::DialectType::ClickHouse)
28897                ) && upper_name.as_str() == "CURRENT_TIMESTAMP")
28898            {
28899                let token = self.advance();
28900                let func = Expression::Function(Box::new(Function {
28901                    name: token.text.clone(), // Preserve original case; generator handles normalization
28902                    args: Vec::new(),
28903                    distinct: false,
28904                    trailing_comments: Vec::new(),
28905                    use_bracket_syntax: false,
28906                    no_parens: true, // These functions were called without parentheses
28907                    quoted: false,
28908                    span: None,
28909                    inferred_type: None,
28910                }));
28911                return self.maybe_parse_subscript(func);
28912            }
28913
28914            let ident = self.expect_identifier_with_quoted()?;
28915            let name = ident.name.clone();
28916            let quoted = ident.quoted;
28917
28918            // Check for function call (skip Teradata FORMAT phrase)
28919            let is_teradata_format_phrase = matches!(
28920                self.config.dialect,
28921                Some(crate::dialects::DialectType::Teradata)
28922            ) && self.check(TokenType::LParen)
28923                && self.check_next(TokenType::Format);
28924            if !is_teradata_format_phrase && self.match_token(TokenType::LParen) {
28925                let upper_name = name.to_ascii_uppercase();
28926                let func_expr = self.parse_typed_function(&name, &upper_name, quoted)?;
28927                let func_expr = self.maybe_parse_clickhouse_parameterized_agg(func_expr)?;
28928                // Check for OVER clause (window function)
28929                return self.maybe_parse_over(func_expr);
28930            }
28931
28932            // Check for qualified name (table.column or table.method())
28933            if self.match_token(TokenType::Dot) {
28934                if self.match_token(TokenType::Star) {
28935                    // table.* with potential modifiers
28936                    let star = self.parse_star_modifiers(Some(ident))?;
28937                    let mut star_expr = Expression::Star(star);
28938                    // ClickHouse: a.* APPLY(func) EXCEPT(col) REPLACE(expr AS col) in any order
28939                    if matches!(
28940                        self.config.dialect,
28941                        Some(crate::dialects::DialectType::ClickHouse)
28942                    ) {
28943                        loop {
28944                            if self.check(TokenType::Apply) {
28945                                self.skip();
28946                                let apply_expr = if self.match_token(TokenType::LParen) {
28947                                    let e = self.parse_expression()?;
28948                                    self.expect(TokenType::RParen)?;
28949                                    e
28950                                } else {
28951                                    self.parse_expression()?
28952                                };
28953                                star_expr =
28954                                    Expression::Apply(Box::new(crate::expressions::Apply {
28955                                        this: Box::new(star_expr),
28956                                        expression: Box::new(apply_expr),
28957                                    }));
28958                            } else if self.check(TokenType::Except)
28959                                || self.check(TokenType::Exclude)
28960                            {
28961                                self.skip();
28962                                self.match_identifier("STRICT");
28963                                if self.match_token(TokenType::LParen) {
28964                                    loop {
28965                                        if self.check(TokenType::RParen) {
28966                                            break;
28967                                        }
28968                                        let _ = self.parse_expression()?;
28969                                        if !self.match_token(TokenType::Comma) {
28970                                            break;
28971                                        }
28972                                    }
28973                                    self.expect(TokenType::RParen)?;
28974                                } else if self.is_identifier_token()
28975                                    || self.is_safe_keyword_as_identifier()
28976                                {
28977                                    let _ = self.parse_expression()?;
28978                                }
28979                            } else if self.check(TokenType::Replace) {
28980                                self.skip();
28981                                self.match_identifier("STRICT");
28982                                if self.match_token(TokenType::LParen) {
28983                                    loop {
28984                                        if self.check(TokenType::RParen) {
28985                                            break;
28986                                        }
28987                                        let _ = self.parse_expression()?;
28988                                        if self.match_token(TokenType::As) {
28989                                            if self.is_identifier_token()
28990                                                || self.is_safe_keyword_as_identifier()
28991                                            {
28992                                                self.skip();
28993                                            }
28994                                        }
28995                                        if !self.match_token(TokenType::Comma) {
28996                                            break;
28997                                        }
28998                                    }
28999                                    self.expect(TokenType::RParen)?;
29000                                } else {
29001                                    let _ = self.parse_expression()?;
29002                                    if self.match_token(TokenType::As) {
29003                                        if self.is_identifier_token()
29004                                            || self.is_safe_keyword_as_identifier()
29005                                        {
29006                                            self.skip();
29007                                        }
29008                                    }
29009                                }
29010                            } else {
29011                                break;
29012                            }
29013                        }
29014                    }
29015                    return Ok(star_expr);
29016                }
29017                // Handle numeric field access: a.1, t.2 (ClickHouse tuple field access)
29018                // Also handle negative: a.-1 (ClickHouse negative tuple index)
29019                if self.check(TokenType::Number) {
29020                    let field_name = self.advance().text;
29021                    let col_expr = Expression::Dot(Box::new(DotAccess {
29022                        this: Expression::boxed_column(Column {
29023                            name: ident,
29024                            table: None,
29025                            join_mark: false,
29026                            trailing_comments: Vec::new(),
29027                            span: None,
29028                            inferred_type: None,
29029                        }),
29030                        field: Identifier::new(field_name),
29031                    }));
29032                    return self.maybe_parse_subscript(col_expr);
29033                }
29034                if matches!(
29035                    self.config.dialect,
29036                    Some(crate::dialects::DialectType::ClickHouse)
29037                ) && self.check(TokenType::Dash)
29038                    && self.current + 1 < self.tokens.len()
29039                    && self.tokens[self.current + 1].token_type == TokenType::Number
29040                {
29041                    self.skip(); // consume -
29042                    let num = self.advance().text;
29043                    let field_name = format!("-{}", num);
29044                    let col_expr = Expression::Dot(Box::new(DotAccess {
29045                        this: Expression::boxed_column(Column {
29046                            name: ident,
29047                            table: None,
29048                            join_mark: false,
29049                            trailing_comments: Vec::new(),
29050                            span: None,
29051                            inferred_type: None,
29052                        }),
29053                        field: Identifier::new(field_name),
29054                    }));
29055                    return self.maybe_parse_subscript(col_expr);
29056                }
29057                // ClickHouse: json.^path — the ^ prefix means "get all nested subcolumns"
29058                if matches!(
29059                    self.config.dialect,
29060                    Some(crate::dialects::DialectType::ClickHouse)
29061                ) && self.check(TokenType::Caret)
29062                {
29063                    self.skip(); // consume ^
29064                    let mut field_name = "^".to_string();
29065                    if self.check(TokenType::Identifier)
29066                        || self.check(TokenType::Var)
29067                        || self.check_keyword()
29068                    {
29069                        field_name.push_str(&self.advance().text);
29070                    }
29071                    let col_expr = Expression::Dot(Box::new(DotAccess {
29072                        this: Expression::boxed_column(Column {
29073                            name: ident,
29074                            table: None,
29075                            join_mark: false,
29076                            trailing_comments: Vec::new(),
29077                            span: None,
29078                            inferred_type: None,
29079                        }),
29080                        field: Identifier::new(field_name),
29081                    }));
29082                    return self.maybe_parse_subscript(col_expr);
29083                }
29084                // Allow keywords as column names (e.g., a.filter, x.update)
29085                let col_ident = self.expect_identifier_or_keyword_with_quoted()?;
29086
29087                // Handle Oracle/Redshift outer join marker (+) BEFORE checking for method call
29088                // This is critical: (+) looks like a method call but is actually a join marker
29089                if self.check(TokenType::LParen) && self.check_next(TokenType::Plus) {
29090                    let saved_pos = self.current;
29091                    if self.match_token(TokenType::LParen)
29092                        && self.match_token(TokenType::Plus)
29093                        && self.match_token(TokenType::RParen)
29094                    {
29095                        let trailing_comments = self.previous_trailing_comments().to_vec();
29096                        let col = Expression::boxed_column(Column {
29097                            name: col_ident,
29098                            table: Some(ident),
29099                            join_mark: true,
29100                            trailing_comments,
29101                            span: None,
29102                            inferred_type: None,
29103                        });
29104                        return self.maybe_parse_subscript(col);
29105                    } else {
29106                        self.current = saved_pos;
29107                    }
29108                }
29109
29110                // Check if this is a method call (column followed by parentheses)
29111                if self.check(TokenType::LParen) {
29112                    // This is a method call like table.EXTRACT() or obj.INT()
29113                    self.skip(); // consume (
29114                    let args = if self.check(TokenType::RParen) {
29115                        Vec::new()
29116                    } else {
29117                        self.parse_expression_list()?
29118                    };
29119                    self.expect(TokenType::RParen)?;
29120                    let method_call = Expression::MethodCall(Box::new(MethodCall {
29121                        this: Expression::boxed_column(Column {
29122                            name: ident.clone(),
29123                            table: None,
29124                            join_mark: false,
29125                            trailing_comments: Vec::new(),
29126                            span: None,
29127                            inferred_type: None,
29128                        }),
29129                        method: col_ident,
29130                        args,
29131                    }));
29132                    return self.maybe_parse_subscript(method_call);
29133                }
29134
29135                // Capture trailing comments from the column name token
29136                let trailing_comments = self.previous_trailing_comments().to_vec();
29137                let col = Expression::boxed_column(Column {
29138                    name: col_ident,
29139                    table: Some(ident),
29140                    join_mark: false,
29141                    trailing_comments,
29142                    span: None,
29143                    inferred_type: None,
29144                });
29145                return self.maybe_parse_subscript(col);
29146            }
29147
29148            // Check for Oracle pseudocolumns (ROWNUM, ROWID, LEVEL, SYSDATE, etc.)
29149            // Note: SQLite treats rowid as a regular column name, not a pseudocolumn
29150            // ClickHouse: skip pseudocolumn parsing as these are regular identifiers
29151            if !quoted
29152                && !matches!(
29153                    self.config.dialect,
29154                    Some(crate::dialects::DialectType::SQLite)
29155                        | Some(crate::dialects::DialectType::ClickHouse)
29156                )
29157            {
29158                if let Some(pseudocolumn_type) = PseudocolumnType::from_str(&name) {
29159                    return Ok(Expression::Pseudocolumn(Pseudocolumn {
29160                        kind: pseudocolumn_type,
29161                    }));
29162                }
29163            }
29164
29165            // Check for lambda expression: x -> body
29166            // But NOT if followed by a string literal (that's JSON extract: col -> '$.path')
29167            if self.check(TokenType::Arrow)
29168                && !self
29169                    .peek_nth(1)
29170                    .map_or(false, |t| t.token_type == TokenType::String)
29171            {
29172                self.skip(); // consume the Arrow token
29173                let body = self.parse_expression()?;
29174                return Ok(Expression::Lambda(Box::new(LambdaExpr {
29175                    parameters: vec![ident],
29176                    body,
29177                    colon: false,
29178                    parameter_types: Vec::new(),
29179                })));
29180            }
29181
29182            // Capture trailing comments from the identifier token
29183            let trailing_comments = self.previous_trailing_comments().to_vec();
29184            let col = Expression::boxed_column(Column {
29185                name: ident,
29186                table: None,
29187                join_mark: false,
29188                trailing_comments,
29189                span: None,
29190                inferred_type: None,
29191            });
29192            return self.maybe_parse_subscript(col);
29193        }
29194
29195        // Exasol-style IF expression: IF condition THEN true_value ELSE false_value ENDIF
29196        // Check for IF not followed by ( (which would be IF function call handled elsewhere)
29197        // This handles: IF age < 18 THEN 'minor' ELSE 'adult' ENDIF
29198        // IMPORTANT: This must be checked BEFORE is_safe_keyword_as_identifier() which would
29199        // treat IF as a column name when not followed by ( or .
29200        // For TSQL/Fabric: IF (cond) BEGIN ... END is an IF statement, not function
29201        if self.check(TokenType::If)
29202            && !self.check_next(TokenType::Dot)
29203            && (!self.check_next(TokenType::LParen) || matches!(
29204                self.config.dialect,
29205                Some(crate::dialects::DialectType::TSQL) | Some(crate::dialects::DialectType::Fabric)
29206            ))
29207        {
29208            let saved_pos = self.current;
29209            self.skip(); // consume IF
29210            if let Some(if_expr) = self.parse_if()? {
29211                return Ok(if_expr);
29212            }
29213            // parse_if() returned None — IF is not an IF expression here,
29214            // restore position so it can be treated as an identifier
29215            self.current = saved_pos;
29216        }
29217
29218        // NEXT VALUE FOR sequence_name [OVER (ORDER BY ...)]
29219        // Must check before treating NEXT as a standalone identifier via is_safe_keyword_as_identifier
29220        if self.check(TokenType::Next)
29221            && self.current + 2 < self.tokens.len()
29222            && self.tokens[self.current + 1]
29223                .text
29224                .eq_ignore_ascii_case("VALUE")
29225            && self.tokens[self.current + 2]
29226                .text
29227                .eq_ignore_ascii_case("FOR")
29228        {
29229            self.skip(); // consume NEXT
29230            if let Some(expr) = self.parse_next_value_for()? {
29231                return Ok(expr);
29232            }
29233        }
29234
29235        // ClickHouse: `from` can be a column name when followed by comma or dot
29236        if matches!(
29237            self.config.dialect,
29238            Some(crate::dialects::DialectType::ClickHouse)
29239        ) && self.check(TokenType::From)
29240            && (self.check_next(TokenType::Comma) || self.check_next(TokenType::Dot))
29241        {
29242            let token = self.advance();
29243            let name = token.text.clone();
29244            if self.match_token(TokenType::Dot) {
29245                // from.col qualified reference
29246                let col_name = self.expect_identifier_or_keyword()?;
29247                return Ok(Expression::Column(Box::new(crate::expressions::Column {
29248                    name: Identifier::new(col_name),
29249                    table: Some(Identifier::new(name)),
29250                    join_mark: false,
29251                    trailing_comments: Vec::new(),
29252                    span: None,
29253                    inferred_type: None,
29254                })));
29255            }
29256            return Ok(Expression::Column(Box::new(crate::expressions::Column {
29257                name: Identifier::new(name),
29258                table: None,
29259                join_mark: false,
29260                trailing_comments: Vec::new(),
29261                span: None,
29262                inferred_type: None,
29263            })));
29264        }
29265
29266        // ClickHouse: `except` as identifier in expression context (set operations are handled at statement level)
29267        // except(args) is already handled above in the MINUS/EXCEPT/INTERSECT function block
29268        if matches!(
29269            self.config.dialect,
29270            Some(crate::dialects::DialectType::ClickHouse)
29271        ) && self.check(TokenType::Except)
29272            && !self.check_next(TokenType::LParen)
29273        {
29274            let token = self.advance();
29275            let name = token.text.clone();
29276            if self.match_token(TokenType::Dot) {
29277                let col_name = self.expect_identifier_or_keyword()?;
29278                return Ok(Expression::Column(Box::new(crate::expressions::Column {
29279                    name: Identifier::new(col_name),
29280                    table: Some(Identifier::new(name)),
29281                    join_mark: false,
29282                    trailing_comments: Vec::new(),
29283                    span: None,
29284                    inferred_type: None,
29285                })));
29286            }
29287            return Ok(Expression::Column(Box::new(crate::expressions::Column {
29288                name: Identifier::new(name),
29289                table: None,
29290                join_mark: false,
29291                trailing_comments: Vec::new(),
29292                span: None,
29293                inferred_type: None,
29294            })));
29295        }
29296
29297        // ClickHouse: structural keywords like FROM, ON, JOIN can be used as identifiers
29298        // in expression context when followed by an operator (e.g., from + 1, on.col)
29299        if matches!(
29300            self.config.dialect,
29301            Some(crate::dialects::DialectType::ClickHouse)
29302        ) && self.peek().token_type.is_keyword()
29303            && !self.is_safe_keyword_as_identifier()
29304        {
29305            let next_tt = self
29306                .peek_nth(1)
29307                .map(|t| t.token_type)
29308                .unwrap_or(TokenType::Semicolon);
29309            // A structural keyword can be used as an identifier when it appears
29310            // in expression context. We detect this by checking what follows.
29311            // Essentially: it's NOT an identifier only if the keyword itself starts
29312            // a clause (e.g., FROM followed by a table name). But when it's followed
29313            // by an operator, comma, close-paren, or even another clause keyword
29314            // (meaning it's the last token in an expression), it's an identifier.
29315            let is_expr_context = !matches!(
29316                next_tt,
29317                TokenType::Identifier
29318                    | TokenType::Var
29319                    | TokenType::QuotedIdentifier
29320                    | TokenType::LParen
29321                    | TokenType::Number
29322                    | TokenType::String
29323            );
29324            if is_expr_context {
29325                let token = self.advance();
29326                return Ok(Expression::boxed_column(Column {
29327                    name: Identifier::new(token.text),
29328                    table: None,
29329                    join_mark: false,
29330                    trailing_comments: Vec::new(),
29331                    span: None,
29332                    inferred_type: None,
29333                }));
29334            }
29335        }
29336        // %s or %(name)s percent parameter (PostgreSQL psycopg2 style)
29337        // Must be checked BEFORE the keyword-as-identifier handler below, since
29338        // Percent is in is_keyword() and is_safe_keyword_as_identifier() returns true for it.
29339        if self.check(TokenType::Percent)
29340            && (
29341                self.check_next(TokenType::Var)  // %s
29342            || self.check_next(TokenType::LParen)
29343                // %(name)s
29344            )
29345        {
29346            self.skip(); // consume %
29347                            // Check for %(name)s - named parameter
29348            if self.match_token(TokenType::LParen) {
29349                // Get the parameter name
29350                if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
29351                    let name = self.advance().text;
29352                    self.expect(TokenType::RParen)?;
29353                    // Expect 's' after the closing paren
29354                    if self.check(TokenType::Var) && self.peek().text == "s" {
29355                        self.skip(); // consume 's'
29356                    }
29357                    return Ok(Expression::Parameter(Box::new(Parameter {
29358                        name: Some(name),
29359                        index: None,
29360                        style: ParameterStyle::Percent,
29361                        quoted: false,
29362                        string_quoted: false,
29363                        expression: None,
29364                    })));
29365                } else {
29366                    return Err(self.parse_error("Expected parameter name after %("));
29367                }
29368            }
29369            // Check for %s - anonymous parameter
29370            if self.check(TokenType::Var) && self.peek().text == "s" {
29371                self.skip(); // consume 's'
29372                return Ok(Expression::Parameter(Box::new(Parameter {
29373                    name: None,
29374                    index: None,
29375                    style: ParameterStyle::Percent,
29376                    quoted: false,
29377                    string_quoted: false,
29378                    expression: None,
29379                })));
29380            }
29381            // Not a parameter - backtrack
29382            self.current -= 1;
29383        }
29384
29385        // Some keywords can be used as identifiers (column names, table names, etc.)
29386        // when they are "safe" keywords that don't affect query structure.
29387        // Structural keywords like FROM, WHERE, JOIN should NOT be usable as identifiers.
29388        if self.is_safe_keyword_as_identifier() {
29389            let token = self.advance();
29390            let name = token.text.clone();
29391
29392            // Check for function call (keyword followed by paren) - skip Teradata FORMAT phrase
29393            let is_teradata_format_phrase = matches!(
29394                self.config.dialect,
29395                Some(crate::dialects::DialectType::Teradata)
29396            ) && self.check(TokenType::LParen)
29397                && self.check_next(TokenType::Format);
29398            if !is_teradata_format_phrase && self.match_token(TokenType::LParen) {
29399                let upper_name = name.to_ascii_uppercase();
29400                let func_expr = self.parse_typed_function(&name, &upper_name, false)?;
29401                let func_expr = self.maybe_parse_clickhouse_parameterized_agg(func_expr)?;
29402                return self.maybe_parse_over(func_expr);
29403            }
29404
29405            // Check for qualified name (keyword.column or keyword.method())
29406            if self.match_token(TokenType::Dot) {
29407                if self.match_token(TokenType::Star) {
29408                    // keyword.* with potential modifiers
29409                    let ident = Identifier::new(name);
29410                    let star = self.parse_star_modifiers(Some(ident))?;
29411                    return Ok(Expression::Star(star));
29412                }
29413                // ClickHouse: json.^path — the ^ prefix means "get all nested subcolumns"
29414                if matches!(
29415                    self.config.dialect,
29416                    Some(crate::dialects::DialectType::ClickHouse)
29417                ) && self.check(TokenType::Caret)
29418                {
29419                    self.skip(); // consume ^
29420                    let mut field_name = "^".to_string();
29421                    if self.check(TokenType::Identifier)
29422                        || self.check(TokenType::Var)
29423                        || self.check_keyword()
29424                    {
29425                        field_name.push_str(&self.advance().text);
29426                    }
29427                    let col = Expression::Dot(Box::new(DotAccess {
29428                        this: Expression::boxed_column(Column {
29429                            name: Identifier::new(name),
29430                            table: None,
29431                            join_mark: false,
29432                            trailing_comments: Vec::new(),
29433                            span: None,
29434                            inferred_type: None,
29435                        }),
29436                        field: Identifier::new(field_name),
29437                    }));
29438                    return self.maybe_parse_subscript(col);
29439                }
29440
29441                // Handle numeric field access: keyword.1, keyword.2 (ClickHouse tuple field access)
29442                if self.check(TokenType::Number) {
29443                    let field_name = self.advance().text;
29444                    let col_expr = Expression::Dot(Box::new(DotAccess {
29445                        this: Expression::boxed_column(Column {
29446                            name: Identifier::new(name),
29447                            table: None,
29448                            join_mark: false,
29449                            trailing_comments: Vec::new(),
29450                            span: None,
29451                            inferred_type: None,
29452                        }),
29453                        field: Identifier::new(field_name),
29454                    }));
29455                    return self.maybe_parse_subscript(col_expr);
29456                }
29457
29458                // Allow keywords as column names
29459                let col_ident = self.expect_identifier_or_keyword_with_quoted()?;
29460
29461                // Check if this is a method call
29462                if self.check(TokenType::LParen) {
29463                    self.skip(); // consume (
29464                    let args = if self.check(TokenType::RParen) {
29465                        Vec::new()
29466                    } else {
29467                        self.parse_expression_list()?
29468                    };
29469                    self.expect(TokenType::RParen)?;
29470                    let method_call = Expression::MethodCall(Box::new(MethodCall {
29471                        this: Expression::Identifier(Identifier::new(name)),
29472                        method: col_ident,
29473                        args,
29474                    }));
29475                    return self.maybe_parse_subscript(method_call);
29476                }
29477
29478                // Capture trailing comments from the column name token
29479                let trailing_comments = self.previous_trailing_comments().to_vec();
29480                let mut col = Expression::boxed_column(Column {
29481                    name: col_ident,
29482                    table: Some(Identifier::new(name)),
29483                    join_mark: false,
29484                    trailing_comments,
29485                    span: None,
29486                    inferred_type: None,
29487                });
29488                // Handle Oracle/Redshift outer join marker (+) after column reference
29489                if self.check(TokenType::LParen) && self.check_next(TokenType::Plus) {
29490                    let saved_pos = self.current;
29491                    if self.match_token(TokenType::LParen)
29492                        && self.match_token(TokenType::Plus)
29493                        && self.match_token(TokenType::RParen)
29494                    {
29495                        if let Expression::Column(ref mut c) = col {
29496                            c.join_mark = true;
29497                        }
29498                    } else {
29499                        self.current = saved_pos;
29500                    }
29501                }
29502                return self.maybe_parse_subscript(col);
29503            }
29504
29505            // Simple identifier (keyword used as column name)
29506            // Capture trailing comments from the keyword token
29507            let trailing_comments = self.previous_trailing_comments().to_vec();
29508            let ident = Identifier::new(name);
29509            let col = Expression::boxed_column(Column {
29510                name: ident,
29511                table: None,
29512                join_mark: false,
29513                trailing_comments,
29514                span: None,
29515                inferred_type: None,
29516            });
29517            return self.maybe_parse_subscript(col);
29518        }
29519
29520        // @@ system variable (MySQL/SQL Server): @@version, @@IDENTITY, @@GLOBAL.var
29521        if self.match_token(TokenType::AtAt) {
29522            // Get the variable name
29523            let name = if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
29524                let mut n = self.advance().text;
29525                // Handle @@scope.variable (e.g., @@GLOBAL.max_connections, @@SESSION.sql_mode)
29526                if self.match_token(TokenType::Dot) {
29527                    if self.check(TokenType::Identifier)
29528                        || self.check(TokenType::Var)
29529                        || self.is_safe_keyword_as_identifier()
29530                    {
29531                        n.push('.');
29532                        n.push_str(&self.advance().text);
29533                    }
29534                }
29535                n
29536            } else if self.check_keyword() {
29537                // Handle @@keyword (e.g., @@sql_mode when sql_mode is a keyword)
29538                self.advance().text
29539            } else {
29540                return Err(self.parse_error("Expected variable name after @@"));
29541            };
29542            return Ok(Expression::Parameter(Box::new(Parameter {
29543                name: Some(name),
29544                index: None,
29545                style: ParameterStyle::DoubleAt,
29546                quoted: false,
29547                string_quoted: false,
29548                expression: None,
29549            })));
29550        }
29551
29552        // @ user variable/parameter: @x, @"x", @JOIN, @'foo'
29553        if self.match_token(TokenType::DAt) {
29554            // Get the variable name - can be identifier, quoted identifier, keyword, or string
29555            let (name, quoted, string_quoted) =
29556                if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
29557                    (self.advance().text, false, false)
29558                } else if self.check(TokenType::QuotedIdentifier) {
29559                    // Quoted identifier like @"x"
29560                    let token = self.advance();
29561                    (token.text, true, false)
29562                } else if self.check(TokenType::String) {
29563                    // String-quoted like @'foo'
29564                    let token = self.advance();
29565                    (token.text, false, true)
29566                } else if self.check(TokenType::Number) {
29567                    // Numeric like @1
29568                    let token = self.advance();
29569                    (token.text, false, false)
29570                } else if self.peek().token_type.is_keyword() {
29571                    // Keyword used as variable name like @JOIN
29572                    let token = self.advance();
29573                    (token.text, false, false)
29574                } else {
29575                    return Err(self.parse_error("Expected variable name after @"));
29576                };
29577            return Ok(Expression::Parameter(Box::new(Parameter {
29578                name: Some(name),
29579                index: None,
29580                style: ParameterStyle::At,
29581                quoted,
29582                string_quoted,
29583                expression: None,
29584            })));
29585        }
29586
29587        // Parameter: ? placeholder or $n positional parameter
29588        if self.check(TokenType::Parameter) {
29589            let token = self.advance();
29590            // Check if this is a positional parameter ($1, $2, etc.) or a plain ? placeholder
29591            if let Ok(index) = token.text.parse::<u32>() {
29592                // Positional parameter like $1, $2 (token text is just the number)
29593                let param = Expression::Parameter(Box::new(Parameter {
29594                    name: None,
29595                    index: Some(index),
29596                    style: ParameterStyle::Dollar,
29597                    quoted: false,
29598                    string_quoted: false,
29599                    expression: None,
29600                }));
29601                // Check for JSON path access: $1:name or dot access: $1.c1
29602                let result = self.parse_colon_json_path(param)?;
29603                return self.maybe_parse_subscript(result);
29604            } else {
29605                // Plain ? placeholder
29606                return Ok(Expression::Placeholder(Placeholder { index: None }));
29607            }
29608        }
29609
29610        // :name or :1 colon parameter
29611        if self.match_token(TokenType::Colon) {
29612            // Check for numeric parameter :1, :2, etc.
29613            if self.check(TokenType::Number) {
29614                let num_token = self.advance();
29615                if let Ok(index) = num_token.text.parse::<u32>() {
29616                    return Ok(Expression::Parameter(Box::new(Parameter {
29617                        name: None,
29618                        index: Some(index),
29619                        style: ParameterStyle::Colon,
29620                        quoted: false,
29621                        string_quoted: false,
29622                        expression: None,
29623                    })));
29624                }
29625                return Err(
29626                    self.parse_error(format!("Invalid colon parameter: :{}", num_token.text))
29627                );
29628            }
29629            // Get the parameter name
29630            if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
29631                let name = self.advance().text;
29632                return Ok(Expression::Parameter(Box::new(Parameter {
29633                    name: Some(name),
29634                    index: None,
29635                    style: ParameterStyle::Colon,
29636                    quoted: false,
29637                    string_quoted: false,
29638                    expression: None,
29639                })));
29640            } else {
29641                return Err(self.parse_error("Expected parameter name after :"));
29642            }
29643        }
29644
29645        // $n dollar parameter: $1, $2, etc.
29646        if self.match_token(TokenType::Dollar) {
29647            // Check for ${identifier} or ${kind:name} template variable syntax (Databricks, Hive)
29648            // Hive supports ${hiveconf:variable_name} syntax
29649            if self.match_token(TokenType::LBrace) {
29650                // Parse the variable name - can be identifier or keyword
29651                if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
29652                    let name_token = self.advance();
29653                    // Check for ${kind:name} syntax (e.g., ${hiveconf:some_var})
29654                    let expression = if self.match_token(TokenType::Colon) {
29655                        if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
29656                            let expr_token = self.advance();
29657                            Some(expr_token.text.clone())
29658                        } else {
29659                            return Err(self.parse_error("Expected identifier after : in ${...}"));
29660                        }
29661                    } else {
29662                        None
29663                    };
29664                    self.expect(TokenType::RBrace)?;
29665                    return Ok(Expression::Parameter(Box::new(Parameter {
29666                        name: Some(name_token.text.clone()),
29667                        index: None,
29668                        style: ParameterStyle::DollarBrace,
29669                        quoted: false,
29670                        string_quoted: false,
29671                        expression,
29672                    })));
29673                } else {
29674                    return Err(self.parse_error("Expected identifier after ${"));
29675                }
29676            }
29677            // Check for number following the dollar sign → positional parameter ($1, $2, etc.)
29678            if self.check(TokenType::Number) {
29679                let num_token = self.advance();
29680                // Parse the number as an index
29681                if let Ok(index) = num_token.text.parse::<u32>() {
29682                    let param_expr = Expression::Parameter(Box::new(Parameter {
29683                        name: None,
29684                        index: Some(index),
29685                        style: ParameterStyle::Dollar,
29686                        quoted: false,
29687                        string_quoted: false,
29688                        expression: None,
29689                    }));
29690                    // Check for JSON path access: $1:name or $1:name:subname
29691                    let result = self.parse_colon_json_path(param_expr)?;
29692                    // Also check for dot access: $1.c1 or $1:name.field
29693                    return self.maybe_parse_subscript(result);
29694                }
29695                // If it's not a valid integer, treat as error
29696                return Err(
29697                    self.parse_error(format!("Invalid dollar parameter: ${}", num_token.text))
29698                );
29699            }
29700            // Check for identifier following the dollar sign → session variable ($x, $query_id, etc.)
29701            if self.check(TokenType::Identifier)
29702                || self.check(TokenType::Var)
29703                || self.is_safe_keyword_as_identifier()
29704            {
29705                let name_token = self.advance();
29706                return Ok(Expression::Parameter(Box::new(Parameter {
29707                    name: Some(name_token.text.clone()),
29708                    index: None,
29709                    style: ParameterStyle::Dollar,
29710                    quoted: false,
29711                    string_quoted: false,
29712                    expression: None,
29713                })));
29714            }
29715            // Just a $ by itself - treat as error
29716            return Err(self.parse_error("Expected number or identifier after $"));
29717        }
29718
29719        // %s or %(name)s percent parameter (PostgreSQL psycopg2 style)
29720        if self.match_token(TokenType::Percent) {
29721            // Check for %(name)s - named parameter
29722            if self.match_token(TokenType::LParen) {
29723                // Get the parameter name
29724                if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
29725                    let name = self.advance().text;
29726                    self.expect(TokenType::RParen)?;
29727                    // Expect 's' after the closing paren
29728                    if self.check(TokenType::Var) && self.peek().text == "s" {
29729                        self.skip(); // consume 's'
29730                    }
29731                    return Ok(Expression::Parameter(Box::new(Parameter {
29732                        name: Some(name),
29733                        index: None,
29734                        style: ParameterStyle::Percent,
29735                        quoted: false,
29736                        string_quoted: false,
29737                        expression: None,
29738                    })));
29739                } else {
29740                    return Err(self.parse_error("Expected parameter name after %("));
29741                }
29742            }
29743            // Check for %s - anonymous parameter
29744            if self.check(TokenType::Var) && self.peek().text == "s" {
29745                self.skip(); // consume 's'
29746                return Ok(Expression::Parameter(Box::new(Parameter {
29747                    name: None,
29748                    index: None,
29749                    style: ParameterStyle::Percent,
29750                    quoted: false,
29751                    string_quoted: false,
29752                    expression: None,
29753                })));
29754            }
29755            // If not followed by 's' or '(', it's not a parameter - error
29756            return Err(self.parse_error("Expected 's' or '(' after % for parameter"));
29757        }
29758
29759        // LEFT, RIGHT, OUTER, FULL, ALL etc. keywords as identifiers when followed by DOT
29760        // e.g., SELECT LEFT.FOO FROM ... or SELECT all.count FROM ...
29761        if (self.check(TokenType::Left)
29762            || self.check(TokenType::Right)
29763            || self.check(TokenType::Outer)
29764            || self.check(TokenType::Full)
29765            || self.check(TokenType::All)
29766            || self.check(TokenType::Only)
29767            || self.check(TokenType::Next)
29768            || self.check(TokenType::If))
29769            && self.check_next(TokenType::Dot)
29770        {
29771            let token = self.advance();
29772            let ident = Identifier::new(token.text);
29773            self.expect(TokenType::Dot)?;
29774            if self.match_token(TokenType::Star) {
29775                let star = self.parse_star_modifiers(Some(ident))?;
29776                return Ok(Expression::Star(star));
29777            }
29778            let col_ident = self.expect_identifier_or_keyword_with_quoted()?;
29779            let trailing_comments = self.previous_trailing_comments().to_vec();
29780            let mut col = Expression::boxed_column(Column {
29781                name: col_ident,
29782                table: Some(ident),
29783                join_mark: false,
29784                trailing_comments,
29785                span: None,
29786                inferred_type: None,
29787            });
29788            // Handle Oracle/Redshift outer join marker (+) after column reference
29789            if self.check(TokenType::LParen) && self.check_next(TokenType::Plus) {
29790                let saved_pos = self.current;
29791                if self.match_token(TokenType::LParen)
29792                    && self.match_token(TokenType::Plus)
29793                    && self.match_token(TokenType::RParen)
29794                {
29795                    if let Expression::Column(ref mut c) = col {
29796                        c.join_mark = true;
29797                    }
29798                } else {
29799                    self.current = saved_pos;
29800                }
29801            }
29802            return self.maybe_parse_subscript(col);
29803        }
29804
29805        // NEXT VALUE FOR sequence_name [OVER (ORDER BY ...)]
29806        // Must check before treating NEXT as a standalone identifier
29807        if self.check(TokenType::Next) {
29808            // NEXT(arg) - pattern navigation function in MATCH_RECOGNIZE
29809            if self.check_next(TokenType::LParen) {
29810                let token = self.advance();
29811                self.skip(); // consume LParen
29812                let args = self.parse_function_args_list()?;
29813                self.expect(TokenType::RParen)?;
29814                return Ok(Expression::Function(Box::new(Function {
29815                    name: token.text,
29816                    args,
29817                    distinct: false,
29818                    trailing_comments: Vec::new(),
29819                    use_bracket_syntax: false,
29820                    no_parens: false,
29821                    quoted: false,
29822                    span: None,
29823                    inferred_type: None,
29824                })));
29825            }
29826        }
29827
29828        // LEFT, RIGHT, OUTER, FULL, ONLY, NEXT as standalone identifiers (not followed by JOIN or LParen)
29829        // e.g., SELECT LEFT FROM ... or SELECT only FROM ...
29830        // If followed by LParen, it's a function call (e.g., NEXT(bar) in MATCH_RECOGNIZE)
29831        if self.can_be_alias_keyword()
29832            && !self.check_next(TokenType::Join)
29833            && !self.check_next(TokenType::LParen)
29834        {
29835            let token = self.advance();
29836            let trailing_comments = self.previous_trailing_comments().to_vec();
29837            let col = Expression::boxed_column(Column {
29838                name: Identifier::new(token.text),
29839                table: None,
29840                join_mark: false,
29841                trailing_comments,
29842                span: None,
29843                inferred_type: None,
29844            });
29845            return self.maybe_parse_subscript(col);
29846        }
29847
29848        Err(self.parse_error(format!("Unexpected token: {:?}", self.peek().token_type)))
29849    }
29850
29851    /// Check if function name is a known aggregate function
29852    fn is_aggregate_function(name: &str) -> bool {
29853        crate::function_registry::is_aggregate_function_name(name)
29854    }
29855
29856    /// Whether the source dialect uses LOG(base, value) order (base first).
29857    /// Default is true. BigQuery, TSQL, Tableau, Fabric use LOG(value, base).
29858    fn log_base_first(&self) -> bool {
29859        !matches!(
29860            self.config.dialect,
29861            Some(crate::dialects::DialectType::BigQuery)
29862                | Some(crate::dialects::DialectType::TSQL)
29863                | Some(crate::dialects::DialectType::Tableau)
29864                | Some(crate::dialects::DialectType::Fabric)
29865        )
29866    }
29867
29868    /// Whether the source dialect treats single-arg LOG(x) as LN(x).
29869    /// These dialects have LOG_DEFAULTS_TO_LN = True in Python sqlglot.
29870    fn log_defaults_to_ln(&self) -> bool {
29871        matches!(
29872            self.config.dialect,
29873            Some(crate::dialects::DialectType::MySQL)
29874                | Some(crate::dialects::DialectType::BigQuery)
29875                | Some(crate::dialects::DialectType::TSQL)
29876                | Some(crate::dialects::DialectType::ClickHouse)
29877                | Some(crate::dialects::DialectType::Hive)
29878                | Some(crate::dialects::DialectType::Spark)
29879                | Some(crate::dialects::DialectType::Databricks)
29880                | Some(crate::dialects::DialectType::Drill)
29881                | Some(crate::dialects::DialectType::Dremio)
29882        )
29883    }
29884
29885    /// Parse the subset of typed functions that are handled via function-registry metadata.
29886    fn try_parse_registry_typed_function(
29887        &mut self,
29888        name: &str,
29889        upper_name: &str,
29890        canonical_upper_name: &str,
29891        quoted: bool,
29892    ) -> Result<Option<Expression>> {
29893        let Some(spec) =
29894            crate::function_registry::typed_function_spec_by_canonical_upper(canonical_upper_name)
29895        else {
29896            return Ok(None);
29897        };
29898
29899        match (spec.parse_kind, spec.canonical_name) {
29900            (crate::function_registry::TypedParseKind::AggregateLike, "COUNT_IF") => {
29901                let distinct = self.match_token(TokenType::Distinct);
29902                let this = self.parse_expression()?;
29903                // ClickHouse: handle AS alias inside countIf args: countIf(expr AS d, pred)
29904                let this = if matches!(
29905                    self.config.dialect,
29906                    Some(crate::dialects::DialectType::ClickHouse)
29907                ) && self.check(TokenType::As)
29908                {
29909                    let next_idx = self.current + 1;
29910                    let after_alias_idx = self.current + 2;
29911                    let is_alias = next_idx < self.tokens.len()
29912                        && (matches!(
29913                            self.tokens[next_idx].token_type,
29914                            TokenType::Identifier | TokenType::Var | TokenType::QuotedIdentifier
29915                        ) || self.tokens[next_idx].token_type.is_keyword())
29916                        && after_alias_idx < self.tokens.len()
29917                        && matches!(
29918                            self.tokens[after_alias_idx].token_type,
29919                            TokenType::RParen | TokenType::Comma
29920                        );
29921                    if is_alias {
29922                        self.skip(); // consume AS
29923                        let alias_token = self.advance();
29924                        Expression::Alias(Box::new(crate::expressions::Alias {
29925                            this,
29926                            alias: Identifier::new(alias_token.text.clone()),
29927                            column_aliases: Vec::new(),
29928                            pre_alias_comments: Vec::new(),
29929                            trailing_comments: Vec::new(),
29930                            inferred_type: None,
29931                        }))
29932                    } else {
29933                        this
29934                    }
29935                } else {
29936                    this
29937                };
29938                if matches!(
29939                    self.config.dialect,
29940                    Some(crate::dialects::DialectType::ClickHouse)
29941                ) && self.match_token(TokenType::Comma)
29942                {
29943                    let mut args = vec![this];
29944                    let arg = self.parse_expression()?;
29945                    // Handle AS alias on subsequent args too
29946                    let arg = if self.check(TokenType::As) {
29947                        let next_idx = self.current + 1;
29948                        let after_alias_idx = self.current + 2;
29949                        let is_alias = next_idx < self.tokens.len()
29950                            && (matches!(
29951                                self.tokens[next_idx].token_type,
29952                                TokenType::Identifier
29953                                    | TokenType::Var
29954                                    | TokenType::QuotedIdentifier
29955                            ) || self.tokens[next_idx].token_type.is_keyword())
29956                            && after_alias_idx < self.tokens.len()
29957                            && matches!(
29958                                self.tokens[after_alias_idx].token_type,
29959                                TokenType::RParen | TokenType::Comma
29960                            );
29961                        if is_alias {
29962                            self.skip(); // consume AS
29963                            let alias_token = self.advance();
29964                            Expression::Alias(Box::new(crate::expressions::Alias {
29965                                this: arg,
29966                                alias: Identifier::new(alias_token.text.clone()),
29967                                column_aliases: Vec::new(),
29968                                pre_alias_comments: Vec::new(),
29969                                trailing_comments: Vec::new(),
29970                                inferred_type: None,
29971                            }))
29972                        } else {
29973                            arg
29974                        }
29975                    } else {
29976                        arg
29977                    };
29978                    args.push(arg);
29979                    while self.match_token(TokenType::Comma) {
29980                        args.push(self.parse_expression()?);
29981                    }
29982                    self.expect(TokenType::RParen)?;
29983                    return Ok(Some(Expression::CombinedAggFunc(Box::new(
29984                        CombinedAggFunc {
29985                            this: Box::new(Expression::Identifier(Identifier::new("countIf"))),
29986                            expressions: args,
29987                        },
29988                    ))));
29989                }
29990                self.expect(TokenType::RParen)?;
29991                let filter = self.parse_filter_clause()?;
29992                Ok(Some(Expression::CountIf(Box::new(AggFunc {
29993                    ignore_nulls: None,
29994                    this,
29995                    distinct,
29996                    filter,
29997                    order_by: Vec::new(),
29998                    having_max: None,
29999                    name: Some(name.to_string()),
30000                    limit: None,
30001                    inferred_type: None,
30002                }))))
30003            }
30004            (crate::function_registry::TypedParseKind::Binary, "STARTS_WITH")
30005            | (crate::function_registry::TypedParseKind::Binary, "ENDS_WITH") => {
30006                let this = self.parse_expression()?;
30007                self.expect(TokenType::Comma)?;
30008                let expression = self.parse_expression()?;
30009                self.expect(TokenType::RParen)?;
30010                let func = BinaryFunc {
30011                    original_name: None,
30012                    this,
30013                    expression,
30014                    inferred_type: None,
30015                };
30016                let expr = match spec.canonical_name {
30017                    "STARTS_WITH" => Expression::StartsWith(Box::new(func)),
30018                    "ENDS_WITH" => Expression::EndsWith(Box::new(func)),
30019                    _ => unreachable!("binary typed parse kind already matched in caller"),
30020                };
30021                Ok(Some(expr))
30022            }
30023            (crate::function_registry::TypedParseKind::Binary, "ATAN2") => {
30024                let this = self.parse_expression()?;
30025                self.expect(TokenType::Comma)?;
30026                let expression = self.parse_expression()?;
30027                self.expect(TokenType::RParen)?;
30028                Ok(Some(Expression::Atan2(Box::new(BinaryFunc {
30029                    original_name: None,
30030                    this,
30031                    expression,
30032                    inferred_type: None,
30033                }))))
30034            }
30035            (crate::function_registry::TypedParseKind::Binary, "MAP_FROM_ARRAYS")
30036            | (crate::function_registry::TypedParseKind::Binary, "MAP_CONTAINS_KEY")
30037            | (crate::function_registry::TypedParseKind::Binary, "ELEMENT_AT") => {
30038                let this = self.parse_expression()?;
30039                self.expect(TokenType::Comma)?;
30040                let expression = self.parse_expression()?;
30041                self.expect(TokenType::RParen)?;
30042                let func = BinaryFunc {
30043                    original_name: None,
30044                    this,
30045                    expression,
30046                    inferred_type: None,
30047                };
30048                let expr = match spec.canonical_name {
30049                    "MAP_FROM_ARRAYS" => Expression::MapFromArrays(Box::new(func)),
30050                    "MAP_CONTAINS_KEY" => Expression::MapContainsKey(Box::new(func)),
30051                    "ELEMENT_AT" => Expression::ElementAt(Box::new(func)),
30052                    _ => unreachable!("binary map parse kind already matched in caller"),
30053                };
30054                Ok(Some(expr))
30055            }
30056            (crate::function_registry::TypedParseKind::Binary, "CONTAINS")
30057            | (crate::function_registry::TypedParseKind::Binary, "MOD")
30058            | (crate::function_registry::TypedParseKind::Binary, "POW") => {
30059                let this = self.parse_expression()?;
30060                self.expect(TokenType::Comma)?;
30061                let expression = self.parse_expression()?;
30062                self.expect(TokenType::RParen)?;
30063                let expr = match spec.canonical_name {
30064                    "CONTAINS" => Expression::Contains(Box::new(BinaryFunc {
30065                        original_name: None,
30066                        this,
30067                        expression,
30068                        inferred_type: None,
30069                    })),
30070                    "MOD" => Expression::ModFunc(Box::new(BinaryFunc {
30071                        original_name: None,
30072                        this,
30073                        expression,
30074                        inferred_type: None,
30075                    })),
30076                    "POW" => Expression::Power(Box::new(BinaryFunc {
30077                        original_name: None,
30078                        this,
30079                        expression,
30080                        inferred_type: None,
30081                    })),
30082                    _ => unreachable!("binary scalar parse kind already matched in caller"),
30083                };
30084                Ok(Some(expr))
30085            }
30086            (crate::function_registry::TypedParseKind::Binary, "ADD_MONTHS")
30087            | (crate::function_registry::TypedParseKind::Binary, "MONTHS_BETWEEN")
30088            | (crate::function_registry::TypedParseKind::Binary, "NEXT_DAY") => {
30089                let this = self.parse_expression()?;
30090                self.expect(TokenType::Comma)?;
30091                let expression = self.parse_expression()?;
30092                if spec.canonical_name == "MONTHS_BETWEEN" && self.match_token(TokenType::Comma) {
30093                    let round_off = self.parse_expression()?;
30094                    self.expect(TokenType::RParen)?;
30095                    return Ok(Some(Expression::Function(Box::new(
30096                        crate::expressions::Function::new(
30097                            "MONTHS_BETWEEN".to_string(),
30098                            vec![this, expression, round_off],
30099                        ),
30100                    ))));
30101                }
30102                self.expect(TokenType::RParen)?;
30103                let func = BinaryFunc {
30104                    original_name: None,
30105                    this,
30106                    expression,
30107                    inferred_type: None,
30108                };
30109                let expr = match spec.canonical_name {
30110                    "ADD_MONTHS" => Expression::AddMonths(Box::new(func)),
30111                    "MONTHS_BETWEEN" => Expression::MonthsBetween(Box::new(func)),
30112                    "NEXT_DAY" => Expression::NextDay(Box::new(func)),
30113                    _ => unreachable!("date binary parse kind already matched in caller"),
30114                };
30115                Ok(Some(expr))
30116            }
30117            (crate::function_registry::TypedParseKind::Binary, "ARRAY_CONTAINS")
30118            | (crate::function_registry::TypedParseKind::Binary, "ARRAY_POSITION")
30119            | (crate::function_registry::TypedParseKind::Binary, "ARRAY_APPEND")
30120            | (crate::function_registry::TypedParseKind::Binary, "ARRAY_PREPEND")
30121            | (crate::function_registry::TypedParseKind::Binary, "ARRAY_UNION")
30122            | (crate::function_registry::TypedParseKind::Binary, "ARRAY_EXCEPT")
30123            | (crate::function_registry::TypedParseKind::Binary, "ARRAY_REMOVE") => {
30124                let this = self.parse_expression()?;
30125                self.expect(TokenType::Comma)?;
30126                let expression = self.parse_expression()?;
30127                self.expect(TokenType::RParen)?;
30128                let func = BinaryFunc {
30129                    original_name: None,
30130                    this,
30131                    expression,
30132                    inferred_type: None,
30133                };
30134                let expr = match spec.canonical_name {
30135                    "ARRAY_CONTAINS" => Expression::ArrayContains(Box::new(func)),
30136                    "ARRAY_POSITION" => Expression::ArrayPosition(Box::new(func)),
30137                    "ARRAY_APPEND" => Expression::ArrayAppend(Box::new(func)),
30138                    "ARRAY_PREPEND" => Expression::ArrayPrepend(Box::new(func)),
30139                    "ARRAY_UNION" => Expression::ArrayUnion(Box::new(func)),
30140                    "ARRAY_EXCEPT" => Expression::ArrayExcept(Box::new(func)),
30141                    "ARRAY_REMOVE" => Expression::ArrayRemove(Box::new(func)),
30142                    _ => unreachable!("array binary parse kind already matched in caller"),
30143                };
30144                Ok(Some(expr))
30145            }
30146            (crate::function_registry::TypedParseKind::Unary, "LENGTH") => {
30147                let this = self.parse_expression()?;
30148                // PostgreSQL: LENGTH(string, encoding) accepts optional second argument
30149                if self.match_token(TokenType::Comma) {
30150                    let encoding = self.parse_expression()?;
30151                    self.expect(TokenType::RParen)?;
30152                    // Store as a regular function to preserve both arguments
30153                    Ok(Some(Expression::Function(Box::new(Function::new(
30154                        upper_name,
30155                        vec![this, encoding],
30156                    )))))
30157                } else {
30158                    self.expect(TokenType::RParen)?;
30159                    Ok(Some(Expression::Length(Box::new(UnaryFunc::new(this)))))
30160                }
30161            }
30162            (crate::function_registry::TypedParseKind::Unary, "LOWER") => {
30163                let this = self.parse_expression_with_clickhouse_alias()?;
30164                self.expect(TokenType::RParen)?;
30165                Ok(Some(Expression::Lower(Box::new(UnaryFunc::new(this)))))
30166            }
30167            (crate::function_registry::TypedParseKind::Unary, "UPPER") => {
30168                let this = self.parse_expression_with_clickhouse_alias()?;
30169                self.expect(TokenType::RParen)?;
30170                Ok(Some(Expression::Upper(Box::new(UnaryFunc::new(this)))))
30171            }
30172            (crate::function_registry::TypedParseKind::Unary, "TYPEOF") => {
30173                let this = self.parse_expression()?;
30174                // ClickHouse: expr AS alias inside function args
30175                let this = self.maybe_clickhouse_alias(this);
30176                if self.match_token(TokenType::Comma) {
30177                    // Preserve additional args via generic function form
30178                    let mut all_args = vec![this];
30179                    let remaining = self.parse_function_arguments()?;
30180                    all_args.extend(remaining);
30181                    self.expect(TokenType::RParen)?;
30182                    Ok(Some(Expression::Function(Box::new(Function {
30183                        name: name.to_string(),
30184                        args: all_args,
30185                        distinct: false,
30186                        trailing_comments: Vec::new(),
30187                        use_bracket_syntax: false,
30188                        no_parens: false,
30189                        quoted: false,
30190                        span: None,
30191                        inferred_type: None,
30192                    }))))
30193                } else {
30194                    self.expect(TokenType::RParen)?;
30195                    Ok(Some(Expression::Typeof(Box::new(UnaryFunc::new(this)))))
30196                }
30197            }
30198            (crate::function_registry::TypedParseKind::Unary, "DAYOFWEEK")
30199            | (crate::function_registry::TypedParseKind::Unary, "DAYOFYEAR")
30200            | (crate::function_registry::TypedParseKind::Unary, "DAYOFMONTH")
30201            | (crate::function_registry::TypedParseKind::Unary, "WEEKOFYEAR") => {
30202                let this = self.parse_expression()?;
30203                self.expect(TokenType::RParen)?;
30204                let func = UnaryFunc::new(this);
30205                let expr = match spec.canonical_name {
30206                    "DAYOFWEEK" => Expression::DayOfWeek(Box::new(func)),
30207                    "DAYOFYEAR" => Expression::DayOfYear(Box::new(func)),
30208                    "DAYOFMONTH" => Expression::DayOfMonth(Box::new(func)),
30209                    "WEEKOFYEAR" => Expression::WeekOfYear(Box::new(func)),
30210                    _ => unreachable!("date-part unary parse kind already matched in caller"),
30211                };
30212                Ok(Some(expr))
30213            }
30214            (crate::function_registry::TypedParseKind::Unary, "SIN")
30215            | (crate::function_registry::TypedParseKind::Unary, "COS")
30216            | (crate::function_registry::TypedParseKind::Unary, "TAN")
30217            | (crate::function_registry::TypedParseKind::Unary, "ASIN")
30218            | (crate::function_registry::TypedParseKind::Unary, "ACOS")
30219            | (crate::function_registry::TypedParseKind::Unary, "ATAN")
30220            | (crate::function_registry::TypedParseKind::Unary, "RADIANS")
30221            | (crate::function_registry::TypedParseKind::Unary, "DEGREES") => {
30222                let this = self.parse_expression()?;
30223                // MySQL: ATAN(y, x) with 2 args is equivalent to ATAN2(y, x)
30224                if spec.canonical_name == "ATAN" && self.match_token(TokenType::Comma) {
30225                    let expression = self.parse_expression()?;
30226                    self.expect(TokenType::RParen)?;
30227                    return Ok(Some(Expression::Atan2(Box::new(BinaryFunc {
30228                        original_name: Some("ATAN".to_string()),
30229                        this,
30230                        expression,
30231                        inferred_type: None,
30232                    }))));
30233                }
30234                self.expect(TokenType::RParen)?;
30235                let func = UnaryFunc::new(this);
30236                let expr = match spec.canonical_name {
30237                    "SIN" => Expression::Sin(Box::new(func)),
30238                    "COS" => Expression::Cos(Box::new(func)),
30239                    "TAN" => Expression::Tan(Box::new(func)),
30240                    "ASIN" => Expression::Asin(Box::new(func)),
30241                    "ACOS" => Expression::Acos(Box::new(func)),
30242                    "ATAN" => Expression::Atan(Box::new(func)),
30243                    "RADIANS" => Expression::Radians(Box::new(func)),
30244                    "DEGREES" => Expression::Degrees(Box::new(func)),
30245                    _ => unreachable!("trig unary parse kind already matched in caller"),
30246                };
30247                Ok(Some(expr))
30248            }
30249            (crate::function_registry::TypedParseKind::Unary, "YEAR")
30250            | (crate::function_registry::TypedParseKind::Unary, "MONTH")
30251            | (crate::function_registry::TypedParseKind::Unary, "DAY")
30252            | (crate::function_registry::TypedParseKind::Unary, "HOUR")
30253            | (crate::function_registry::TypedParseKind::Unary, "MINUTE")
30254            | (crate::function_registry::TypedParseKind::Unary, "SECOND")
30255            | (crate::function_registry::TypedParseKind::Unary, "DAYOFWEEK_ISO")
30256            | (crate::function_registry::TypedParseKind::Unary, "QUARTER")
30257            | (crate::function_registry::TypedParseKind::Unary, "EPOCH")
30258            | (crate::function_registry::TypedParseKind::Unary, "EPOCH_MS") => {
30259                let this = self.parse_expression()?;
30260                self.expect(TokenType::RParen)?;
30261                let func = UnaryFunc::new(this);
30262                let expr = match spec.canonical_name {
30263                    "YEAR" => Expression::Year(Box::new(func)),
30264                    "MONTH" => Expression::Month(Box::new(func)),
30265                    "DAY" => Expression::Day(Box::new(func)),
30266                    "HOUR" => Expression::Hour(Box::new(func)),
30267                    "MINUTE" => Expression::Minute(Box::new(func)),
30268                    "SECOND" => Expression::Second(Box::new(func)),
30269                    "DAYOFWEEK_ISO" => Expression::DayOfWeekIso(Box::new(func)),
30270                    "QUARTER" => Expression::Quarter(Box::new(func)),
30271                    "EPOCH" => Expression::Epoch(Box::new(func)),
30272                    "EPOCH_MS" => Expression::EpochMs(Box::new(func)),
30273                    _ => unreachable!("date unary parse kind already matched in caller"),
30274                };
30275                Ok(Some(expr))
30276            }
30277            (crate::function_registry::TypedParseKind::Unary, "ARRAY_LENGTH")
30278            | (crate::function_registry::TypedParseKind::Unary, "ARRAY_SIZE")
30279            | (crate::function_registry::TypedParseKind::Unary, "CARDINALITY")
30280            | (crate::function_registry::TypedParseKind::Unary, "ARRAY_REVERSE")
30281            | (crate::function_registry::TypedParseKind::Unary, "ARRAY_DISTINCT")
30282            | (crate::function_registry::TypedParseKind::Unary, "ARRAY_COMPACT")
30283            | (crate::function_registry::TypedParseKind::Unary, "EXPLODE")
30284            | (crate::function_registry::TypedParseKind::Unary, "EXPLODE_OUTER") => {
30285                let this = self.parse_expression()?;
30286                // PostgreSQL ARRAY_LENGTH and ARRAY_SIZE can take a second dimension arg.
30287                // Preserve that by falling back to generic function form for 2-arg usage.
30288                if (spec.canonical_name == "ARRAY_LENGTH" || spec.canonical_name == "ARRAY_SIZE")
30289                    && self.match_token(TokenType::Comma)
30290                {
30291                    let dimension = self.parse_expression()?;
30292                    self.expect(TokenType::RParen)?;
30293                    return Ok(Some(Expression::Function(Box::new(Function {
30294                        name: name.to_string(),
30295                        args: vec![this, dimension],
30296                        distinct: false,
30297                        trailing_comments: Vec::new(),
30298                        use_bracket_syntax: false,
30299                        no_parens: false,
30300                        quoted: false,
30301                        span: None,
30302                        inferred_type: None,
30303                    }))));
30304                }
30305                self.expect(TokenType::RParen)?;
30306                let func = UnaryFunc::new(this);
30307                let expr = match spec.canonical_name {
30308                    "ARRAY_LENGTH" => Expression::ArrayLength(Box::new(func)),
30309                    "ARRAY_SIZE" => Expression::ArraySize(Box::new(func)),
30310                    "CARDINALITY" => Expression::Cardinality(Box::new(func)),
30311                    "ARRAY_REVERSE" => Expression::ArrayReverse(Box::new(func)),
30312                    "ARRAY_DISTINCT" => Expression::ArrayDistinct(Box::new(func)),
30313                    "ARRAY_COMPACT" => Expression::ArrayCompact(Box::new(func)),
30314                    "EXPLODE" => Expression::Explode(Box::new(func)),
30315                    "EXPLODE_OUTER" => Expression::ExplodeOuter(Box::new(func)),
30316                    _ => unreachable!("array unary parse kind already matched in caller"),
30317                };
30318                Ok(Some(expr))
30319            }
30320            (crate::function_registry::TypedParseKind::Unary, "MAP_FROM_ENTRIES")
30321            | (crate::function_registry::TypedParseKind::Unary, "MAP_KEYS")
30322            | (crate::function_registry::TypedParseKind::Unary, "MAP_VALUES") => {
30323                let this = self.parse_expression()?;
30324                self.expect(TokenType::RParen)?;
30325                let func = UnaryFunc::new(this);
30326                let expr = match spec.canonical_name {
30327                    "MAP_FROM_ENTRIES" => Expression::MapFromEntries(Box::new(func)),
30328                    "MAP_KEYS" => Expression::MapKeys(Box::new(func)),
30329                    "MAP_VALUES" => Expression::MapValues(Box::new(func)),
30330                    _ => unreachable!("map unary parse kind already matched in caller"),
30331                };
30332                Ok(Some(expr))
30333            }
30334            (crate::function_registry::TypedParseKind::Unary, "ABS") => {
30335                let this = self.parse_expression_with_clickhouse_alias()?;
30336                self.expect(TokenType::RParen)?;
30337                Ok(Some(Expression::Abs(Box::new(UnaryFunc::new(this)))))
30338            }
30339            (crate::function_registry::TypedParseKind::Unary, "SQRT")
30340            | (crate::function_registry::TypedParseKind::Unary, "EXP")
30341            | (crate::function_registry::TypedParseKind::Unary, "LN") => {
30342                let this = self.parse_expression()?;
30343                self.expect(TokenType::RParen)?;
30344                let expr = match spec.canonical_name {
30345                    "SQRT" => Expression::Sqrt(Box::new(UnaryFunc::new(this))),
30346                    "EXP" => Expression::Exp(Box::new(UnaryFunc::new(this))),
30347                    "LN" => Expression::Ln(Box::new(UnaryFunc::new(this))),
30348                    _ => unreachable!("math unary parse kind already matched in caller"),
30349                };
30350                Ok(Some(expr))
30351            }
30352            (crate::function_registry::TypedParseKind::Variadic, "TO_NUMBER")
30353            | (crate::function_registry::TypedParseKind::Variadic, "TRY_TO_NUMBER") => {
30354                let args = self.parse_expression_list()?;
30355                self.expect(TokenType::RParen)?;
30356                let this = args.get(0).cloned().unwrap_or(Expression::Null(Null {}));
30357                let format = args.get(1).cloned().map(Box::new);
30358                let precision = args.get(2).cloned().map(Box::new);
30359                let scale = args.get(3).cloned().map(Box::new);
30360                let safe = if spec.canonical_name == "TRY_TO_NUMBER" {
30361                    Some(Box::new(Expression::Boolean(BooleanLiteral {
30362                        value: true,
30363                    })))
30364                } else {
30365                    None
30366                };
30367                Ok(Some(Expression::ToNumber(Box::new(ToNumber {
30368                    this: Box::new(this),
30369                    format,
30370                    nlsparam: None,
30371                    precision,
30372                    scale,
30373                    safe,
30374                    safe_name: None,
30375                }))))
30376            }
30377            (crate::function_registry::TypedParseKind::Variadic, "SUBSTRING") => {
30378                let this = self.parse_expression()?;
30379                // ClickHouse: implicit/explicit alias: substring('1234' lhs FROM 2) or substring('1234' AS lhs FROM 2)
30380                let this = self.try_clickhouse_func_arg_alias(this);
30381
30382                // Check for SQL standard FROM syntax: SUBSTRING(str FROM pos [FOR len])
30383                if self.match_token(TokenType::From) {
30384                    let start = self.parse_expression()?;
30385                    let start = self.try_clickhouse_func_arg_alias(start);
30386                    let length = if self.match_token(TokenType::For) {
30387                        let len = self.parse_expression()?;
30388                        Some(self.try_clickhouse_func_arg_alias(len))
30389                    } else {
30390                        None
30391                    };
30392                    self.expect(TokenType::RParen)?;
30393                    Ok(Some(Expression::Substring(Box::new(SubstringFunc {
30394                        this,
30395                        start,
30396                        length,
30397                        from_for_syntax: true,
30398                    }))))
30399                } else if self.match_token(TokenType::For) {
30400                    // PostgreSQL: SUBSTRING(str FOR len) or SUBSTRING(str FOR len FROM pos)
30401                    let length_expr = self.parse_expression()?;
30402                    let length_expr = self.try_clickhouse_func_arg_alias(length_expr);
30403                    let start = if self.match_token(TokenType::From) {
30404                        let s = self.parse_expression()?;
30405                        self.try_clickhouse_func_arg_alias(s)
30406                    } else {
30407                        // No FROM, use 1 as default start position
30408                        Expression::Literal(Literal::Number("1".to_string()))
30409                    };
30410                    self.expect(TokenType::RParen)?;
30411                    Ok(Some(Expression::Substring(Box::new(SubstringFunc {
30412                        this,
30413                        start,
30414                        length: Some(length_expr),
30415                        from_for_syntax: true,
30416                    }))))
30417                } else if self.match_token(TokenType::Comma) {
30418                    // Comma-separated syntax: SUBSTRING(str, pos) or SUBSTRING(str, pos, len)
30419                    let start = self.parse_expression()?;
30420                    let start = self.try_clickhouse_func_arg_alias(start);
30421                    let length = if self.match_token(TokenType::Comma) {
30422                        let len = self.parse_expression()?;
30423                        Some(self.try_clickhouse_func_arg_alias(len))
30424                    } else {
30425                        None
30426                    };
30427                    self.expect(TokenType::RParen)?;
30428                    Ok(Some(Expression::Substring(Box::new(SubstringFunc {
30429                        this,
30430                        start,
30431                        length,
30432                        from_for_syntax: false,
30433                    }))))
30434                } else {
30435                    // Just SUBSTRING(str) with no other args - unusual but handle it
30436                    self.expect(TokenType::RParen)?;
30437                    // Treat as function call
30438                    Ok(Some(Expression::Function(Box::new(Function {
30439                        name: name.to_string(),
30440                        args: vec![this],
30441                        distinct: false,
30442                        trailing_comments: Vec::new(),
30443                        use_bracket_syntax: false,
30444                        no_parens: false,
30445                        quoted: false,
30446                        span: None,
30447                        inferred_type: None,
30448                    }))))
30449                }
30450            }
30451            (crate::function_registry::TypedParseKind::Variadic, "DATE_PART") => {
30452                let part = self.parse_expression()?;
30453                // For TSQL/Fabric, normalize date part aliases (e.g., "dd" -> DAY)
30454                let mut part = if matches!(
30455                    self.config.dialect,
30456                    Some(crate::dialects::DialectType::TSQL)
30457                        | Some(crate::dialects::DialectType::Fabric)
30458                ) {
30459                    self.normalize_tsql_date_part(part)
30460                } else {
30461                    part
30462                };
30463                // Accept both FROM and comma as separator (Snowflake supports both syntaxes)
30464                if !self.match_token(TokenType::From) && !self.match_token(TokenType::Comma) {
30465                    return Err(self.parse_error("Expected FROM or comma in DATE_PART"));
30466                }
30467                let from_expr = self.parse_expression()?;
30468                self.expect(TokenType::RParen)?;
30469                if matches!(
30470                    self.config.dialect,
30471                    Some(crate::dialects::DialectType::Snowflake)
30472                ) {
30473                    if self
30474                        .try_parse_date_part_field_identifier_expr(&part)
30475                        .is_some()
30476                    {
30477                        part = self.convert_date_part_identifier_expr_to_var(part);
30478                    }
30479                }
30480                Ok(Some(Expression::Function(Box::new(Function {
30481                    name: "DATE_PART".to_string(),
30482                    args: vec![part, from_expr],
30483                    distinct: false,
30484                    trailing_comments: Vec::new(),
30485                    use_bracket_syntax: false,
30486                    no_parens: false,
30487                    quoted: false,
30488                    span: None,
30489                    inferred_type: None,
30490                }))))
30491            }
30492            (crate::function_registry::TypedParseKind::Variadic, "DATEADD") => {
30493                let mut first_arg = self.parse_expression()?;
30494                first_arg = self.try_clickhouse_func_arg_alias(first_arg);
30495                self.expect(TokenType::Comma)?;
30496                let second_arg = self.parse_expression()?;
30497                let second_arg = self.try_clickhouse_func_arg_alias(second_arg);
30498
30499                // Check if there's a third argument (traditional 3-arg syntax)
30500                if self.match_token(TokenType::Comma) {
30501                    let third_arg = self.parse_expression()?;
30502                    let third_arg = self.try_clickhouse_func_arg_alias(third_arg);
30503                    self.expect(TokenType::RParen)?;
30504                    if matches!(
30505                        self.config.dialect,
30506                        Some(crate::dialects::DialectType::Snowflake)
30507                    ) {
30508                        if self
30509                            .try_parse_date_part_unit_identifier_expr(&first_arg)
30510                            .is_some()
30511                        {
30512                            first_arg = self.convert_date_part_identifier_expr_to_var(first_arg);
30513                        }
30514                    }
30515                    Ok(Some(Expression::Function(Box::new(Function {
30516                        name: name.to_string(),
30517                        args: vec![first_arg, second_arg, third_arg],
30518                        distinct: false,
30519                        trailing_comments: Vec::new(),
30520                        use_bracket_syntax: false,
30521                        no_parens: false,
30522                        quoted: false,
30523                        span: None,
30524                        inferred_type: None,
30525                    }))))
30526                } else {
30527                    // BigQuery 2-arg syntax: DATE_ADD(date, interval)
30528                    self.expect(TokenType::RParen)?;
30529                    Ok(Some(Expression::Function(Box::new(Function {
30530                        name: name.to_string(),
30531                        args: vec![first_arg, second_arg],
30532                        distinct: false,
30533                        trailing_comments: Vec::new(),
30534                        use_bracket_syntax: false,
30535                        no_parens: false,
30536                        quoted: false,
30537                        span: None,
30538                        inferred_type: None,
30539                    }))))
30540                }
30541            }
30542            (crate::function_registry::TypedParseKind::Variadic, "DATEDIFF") => {
30543                // First argument (can be unit for DATEDIFF/TIMESTAMPDIFF or datetime for TIMEDIFF)
30544                let first_arg = self.parse_expression()?;
30545                let first_arg = self.try_clickhouse_func_arg_alias(first_arg);
30546                self.expect(TokenType::Comma)?;
30547                let second_arg = self.parse_expression()?;
30548                let second_arg = self.try_clickhouse_func_arg_alias(second_arg);
30549                // Third argument is optional (SQLite TIMEDIFF only takes 2 args)
30550                let mut args = if self.match_token(TokenType::Comma) {
30551                    let third_arg = self.parse_expression()?;
30552                    let third_arg = self.try_clickhouse_func_arg_alias(third_arg);
30553                    vec![first_arg, second_arg, third_arg]
30554                } else {
30555                    vec![first_arg, second_arg]
30556                };
30557                // ClickHouse: optional 4th timezone argument for dateDiff
30558                while self.match_token(TokenType::Comma) {
30559                    let arg = self.parse_expression()?;
30560                    args.push(self.try_clickhouse_func_arg_alias(arg));
30561                }
30562                self.expect(TokenType::RParen)?;
30563                if matches!(
30564                    self.config.dialect,
30565                    Some(crate::dialects::DialectType::Snowflake)
30566                ) && args.len() == 3
30567                {
30568                    if let Some(unit) = self.try_parse_date_part_unit_expr(&args[0]) {
30569                        return Ok(Some(Expression::DateDiff(Box::new(DateDiffFunc {
30570                            this: args[2].clone(),
30571                            expression: args[1].clone(),
30572                            unit: Some(unit),
30573                        }))));
30574                    }
30575                }
30576                Ok(Some(Expression::Function(Box::new(Function {
30577                    name: name.to_string(),
30578                    args,
30579                    distinct: false,
30580                    trailing_comments: Vec::new(),
30581                    use_bracket_syntax: false,
30582                    no_parens: false,
30583                    quoted: false,
30584                    span: None,
30585                    inferred_type: None,
30586                }))))
30587            }
30588            (crate::function_registry::TypedParseKind::Variadic, "RANDOM") => {
30589                // RANDOM() - no args, RANDOM(seed) - Snowflake, RANDOM(lower, upper) - Teradata
30590                if self.check(TokenType::RParen) {
30591                    self.expect(TokenType::RParen)?;
30592                    Ok(Some(Expression::Random(Random)))
30593                } else {
30594                    let first = self.parse_expression()?;
30595                    if self.match_token(TokenType::Comma) {
30596                        let second = self.parse_expression()?;
30597                        self.expect(TokenType::RParen)?;
30598                        Ok(Some(Expression::Rand(Box::new(Rand {
30599                            seed: None,
30600                            lower: Some(Box::new(first)),
30601                            upper: Some(Box::new(second)),
30602                        }))))
30603                    } else {
30604                        self.expect(TokenType::RParen)?;
30605                        Ok(Some(Expression::Rand(Box::new(Rand {
30606                            seed: Some(Box::new(first)),
30607                            lower: None,
30608                            upper: None,
30609                        }))))
30610                    }
30611                }
30612            }
30613            (crate::function_registry::TypedParseKind::Variadic, "RAND") => {
30614                let seed = if self.check(TokenType::RParen) {
30615                    None
30616                } else {
30617                    Some(Box::new(self.parse_expression()?))
30618                };
30619                self.expect(TokenType::RParen)?;
30620                Ok(Some(Expression::Rand(Box::new(Rand {
30621                    seed,
30622                    lower: None,
30623                    upper: None,
30624                }))))
30625            }
30626            (crate::function_registry::TypedParseKind::Variadic, "PI") => {
30627                self.expect(TokenType::RParen)?;
30628                Ok(Some(Expression::Pi(Pi)))
30629            }
30630            (crate::function_registry::TypedParseKind::Variadic, "LAST_DAY") => {
30631                let this = self.parse_expression()?;
30632                let unit = if self.match_token(TokenType::Comma) {
30633                    Some(self.parse_datetime_field()?)
30634                } else {
30635                    None
30636                };
30637                self.expect(TokenType::RParen)?;
30638                Ok(Some(Expression::LastDay(Box::new(LastDayFunc {
30639                    this,
30640                    unit,
30641                }))))
30642            }
30643            (crate::function_registry::TypedParseKind::Variadic, "POSITION") => {
30644                let expr = self
30645                    .parse_position()?
30646                    .ok_or_else(|| self.parse_error("Expected expression in POSITION"))?;
30647                self.expect(TokenType::RParen)?;
30648                Ok(Some(expr))
30649            }
30650            (crate::function_registry::TypedParseKind::Variadic, "STRPOS") => {
30651                let this = self.parse_expression()?;
30652                self.expect(TokenType::Comma)?;
30653                let substr = self.parse_expression()?;
30654                let occurrence = if self.match_token(TokenType::Comma) {
30655                    Some(Box::new(self.parse_expression()?))
30656                } else {
30657                    None
30658                };
30659                self.expect(TokenType::RParen)?;
30660                Ok(Some(Expression::StrPosition(Box::new(StrPosition {
30661                    this: Box::new(this),
30662                    substr: Some(Box::new(substr)),
30663                    position: None,
30664                    occurrence,
30665                }))))
30666            }
30667            (crate::function_registry::TypedParseKind::Variadic, "LOCATE") => {
30668                if self.check(TokenType::RParen) {
30669                    self.skip();
30670                    return Ok(Some(Expression::Function(Box::new(Function {
30671                        name: name.to_string(),
30672                        args: vec![],
30673                        distinct: false,
30674                        trailing_comments: Vec::new(),
30675                        use_bracket_syntax: false,
30676                        no_parens: false,
30677                        quoted: false,
30678                        span: None,
30679                        inferred_type: None,
30680                    }))));
30681                }
30682                let first = self.parse_expression()?;
30683                if !self.check(TokenType::Comma) && self.check(TokenType::RParen) {
30684                    self.skip();
30685                    return Ok(Some(Expression::Function(Box::new(Function {
30686                        name: name.to_string(),
30687                        args: vec![first],
30688                        distinct: false,
30689                        trailing_comments: Vec::new(),
30690                        use_bracket_syntax: false,
30691                        no_parens: false,
30692                        quoted: false,
30693                        span: None,
30694                        inferred_type: None,
30695                    }))));
30696                }
30697                self.expect(TokenType::Comma)?;
30698                let second = self.parse_expression()?;
30699                let position = if self.match_token(TokenType::Comma) {
30700                    Some(Box::new(self.parse_expression()?))
30701                } else {
30702                    None
30703                };
30704                self.expect(TokenType::RParen)?;
30705                Ok(Some(Expression::StrPosition(Box::new(StrPosition {
30706                    this: Box::new(second),
30707                    substr: Some(Box::new(first)),
30708                    position,
30709                    occurrence: None,
30710                }))))
30711            }
30712            (crate::function_registry::TypedParseKind::Variadic, "INSTR") => {
30713                let first = self.parse_expression()?;
30714                self.expect(TokenType::Comma)?;
30715                let second = self.parse_expression()?;
30716                let position = if self.match_token(TokenType::Comma) {
30717                    Some(Box::new(self.parse_expression()?))
30718                } else {
30719                    None
30720                };
30721                self.expect(TokenType::RParen)?;
30722                Ok(Some(Expression::StrPosition(Box::new(StrPosition {
30723                    this: Box::new(first),
30724                    substr: Some(Box::new(second)),
30725                    position,
30726                    occurrence: None,
30727                }))))
30728            }
30729            (crate::function_registry::TypedParseKind::Variadic, "NORMALIZE") => {
30730                let this = self.parse_expression()?;
30731                let form = if self.match_token(TokenType::Comma) {
30732                    Some(Box::new(self.parse_expression()?))
30733                } else {
30734                    None
30735                };
30736                self.expect(TokenType::RParen)?;
30737                Ok(Some(Expression::Normalize(Box::new(Normalize {
30738                    this: Box::new(this),
30739                    form,
30740                    is_casefold: None,
30741                }))))
30742            }
30743            (crate::function_registry::TypedParseKind::Variadic, "INITCAP") => {
30744                let this = self.parse_expression()?;
30745                let delimiter = if self.match_token(TokenType::Comma) {
30746                    Some(Box::new(self.parse_expression()?))
30747                } else {
30748                    None
30749                };
30750                self.expect(TokenType::RParen)?;
30751                if let Some(delim) = delimiter {
30752                    Ok(Some(Expression::Function(Box::new(Function::new(
30753                        "INITCAP".to_string(),
30754                        vec![this, *delim],
30755                    )))))
30756                } else {
30757                    Ok(Some(Expression::Initcap(Box::new(UnaryFunc::new(this)))))
30758                }
30759            }
30760            (crate::function_registry::TypedParseKind::Variadic, "FLOOR") => {
30761                let this = self.parse_expression()?;
30762                let to = if self.match_token(TokenType::To) {
30763                    self.parse_var()?
30764                } else {
30765                    None
30766                };
30767                let scale = if to.is_none() && self.match_token(TokenType::Comma) {
30768                    Some(self.parse_expression()?)
30769                } else {
30770                    None
30771                };
30772                if self.check(TokenType::Comma) {
30773                    let mut args = vec![this];
30774                    if let Some(s) = scale {
30775                        args.push(s);
30776                    }
30777                    while self.match_token(TokenType::Comma) {
30778                        args.push(self.parse_expression()?);
30779                    }
30780                    self.expect(TokenType::RParen)?;
30781                    return Ok(Some(Expression::Function(Box::new(Function {
30782                        name: name.to_string(),
30783                        args,
30784                        distinct: false,
30785                        trailing_comments: Vec::new(),
30786                        use_bracket_syntax: false,
30787                        no_parens: false,
30788                        quoted: false,
30789                        span: None,
30790                        inferred_type: None,
30791                    }))));
30792                }
30793                self.expect(TokenType::RParen)?;
30794                Ok(Some(Expression::Floor(Box::new(FloorFunc {
30795                    this,
30796                    scale,
30797                    to,
30798                }))))
30799            }
30800            (crate::function_registry::TypedParseKind::Variadic, "LOG") => {
30801                let first = self.parse_expression()?;
30802                if self.match_token(TokenType::Comma) {
30803                    let second = self.parse_expression()?;
30804                    self.expect(TokenType::RParen)?;
30805                    let (value, base) = if self.log_base_first() {
30806                        (second, first)
30807                    } else {
30808                        (first, second)
30809                    };
30810                    Ok(Some(Expression::Log(Box::new(LogFunc {
30811                        this: value,
30812                        base: Some(base),
30813                    }))))
30814                } else {
30815                    self.expect(TokenType::RParen)?;
30816                    if self.log_defaults_to_ln() {
30817                        Ok(Some(Expression::Ln(Box::new(UnaryFunc::new(first)))))
30818                    } else {
30819                        Ok(Some(Expression::Log(Box::new(LogFunc {
30820                            this: first,
30821                            base: None,
30822                        }))))
30823                    }
30824                }
30825            }
30826            (crate::function_registry::TypedParseKind::Variadic, "FLATTEN") => {
30827                let args = self.parse_function_arguments()?;
30828                self.expect(TokenType::RParen)?;
30829                Ok(Some(Expression::Function(Box::new(Function {
30830                    name: name.to_string(),
30831                    args,
30832                    distinct: false,
30833                    trailing_comments: Vec::new(),
30834                    use_bracket_syntax: false,
30835                    no_parens: false,
30836                    quoted: false,
30837                    span: None,
30838                    inferred_type: None,
30839                }))))
30840            }
30841            (crate::function_registry::TypedParseKind::Variadic, "ARRAY_INTERSECT") => {
30842                let mut expressions = vec![self.parse_expression()?];
30843                while self.match_token(TokenType::Comma) {
30844                    expressions.push(self.parse_expression()?);
30845                }
30846                self.expect(TokenType::RParen)?;
30847                Ok(Some(Expression::ArrayIntersect(Box::new(VarArgFunc {
30848                    expressions,
30849                    original_name: Some(name.to_string()),
30850                    inferred_type: None,
30851                }))))
30852            }
30853            (crate::function_registry::TypedParseKind::Variadic, "CURRENT_SCHEMAS") => {
30854                let args = if self.check(TokenType::RParen) {
30855                    Vec::new()
30856                } else {
30857                    vec![self.parse_expression()?]
30858                };
30859                self.expect(TokenType::RParen)?;
30860                Ok(Some(Expression::CurrentSchemas(Box::new(CurrentSchemas {
30861                    this: args.into_iter().next().map(Box::new),
30862                }))))
30863            }
30864            (crate::function_registry::TypedParseKind::Variadic, "COALESCE") => {
30865                let args = if self.check(TokenType::RParen) {
30866                    Vec::new()
30867                } else {
30868                    self.parse_expression_list()?
30869                };
30870                self.expect(TokenType::RParen)?;
30871                Ok(Some(Expression::Coalesce(Box::new(
30872                    crate::expressions::VarArgFunc {
30873                        original_name: None,
30874                        expressions: args,
30875                        inferred_type: None,
30876                    },
30877                ))))
30878            }
30879            (crate::function_registry::TypedParseKind::Variadic, "IFNULL") => {
30880                let args = self.parse_expression_list()?;
30881                self.expect(TokenType::RParen)?;
30882                if args.len() >= 2 {
30883                    Ok(Some(Expression::Coalesce(Box::new(
30884                        crate::expressions::VarArgFunc {
30885                            original_name: Some("IFNULL".to_string()),
30886                            expressions: args,
30887                            inferred_type: None,
30888                        },
30889                    ))))
30890                } else {
30891                    Ok(Some(Expression::Function(Box::new(Function {
30892                        name: name.to_string(),
30893                        args,
30894                        distinct: false,
30895                        trailing_comments: Vec::new(),
30896                        use_bracket_syntax: false,
30897                        no_parens: false,
30898                        quoted: false,
30899                        span: None,
30900                        inferred_type: None,
30901                    }))))
30902                }
30903            }
30904            (crate::function_registry::TypedParseKind::Variadic, "NVL") => {
30905                let args = self.parse_expression_list()?;
30906                self.expect(TokenType::RParen)?;
30907                if args.len() > 2 {
30908                    Ok(Some(Expression::Function(Box::new(Function {
30909                        name: "COALESCE".to_string(),
30910                        args,
30911                        distinct: false,
30912                        trailing_comments: Vec::new(),
30913                        use_bracket_syntax: false,
30914                        no_parens: false,
30915                        quoted: false,
30916                        span: None,
30917                        inferred_type: None,
30918                    }))))
30919                } else if args.len() == 2 {
30920                    Ok(Some(Expression::Nvl(Box::new(
30921                        crate::expressions::BinaryFunc {
30922                            original_name: Some("NVL".to_string()),
30923                            this: args[0].clone(),
30924                            expression: args[1].clone(),
30925                            inferred_type: None,
30926                        },
30927                    ))))
30928                } else {
30929                    Ok(Some(Expression::Function(Box::new(Function {
30930                        name: name.to_string(),
30931                        args,
30932                        distinct: false,
30933                        trailing_comments: Vec::new(),
30934                        use_bracket_syntax: false,
30935                        no_parens: false,
30936                        quoted: false,
30937                        span: None,
30938                        inferred_type: None,
30939                    }))))
30940                }
30941            }
30942            (crate::function_registry::TypedParseKind::Variadic, "NVL2") => {
30943                let args = self.parse_expression_list()?;
30944                self.expect(TokenType::RParen)?;
30945                if args.len() >= 3 {
30946                    Ok(Some(Expression::Nvl2(Box::new(
30947                        crate::expressions::Nvl2Func {
30948                            this: args[0].clone(),
30949                            true_value: args[1].clone(),
30950                            false_value: args[2].clone(),
30951                            inferred_type: None,
30952                        },
30953                    ))))
30954                } else {
30955                    Ok(Some(Expression::Function(Box::new(Function {
30956                        name: name.to_string(),
30957                        args,
30958                        distinct: false,
30959                        trailing_comments: Vec::new(),
30960                        use_bracket_syntax: false,
30961                        no_parens: false,
30962                        quoted: false,
30963                        span: None,
30964                        inferred_type: None,
30965                    }))))
30966                }
30967            }
30968            (crate::function_registry::TypedParseKind::Variadic, "EXTRACT") => {
30969                if matches!(
30970                    self.config.dialect,
30971                    Some(crate::dialects::DialectType::ClickHouse)
30972                ) && (self.check(TokenType::Identifier)
30973                    || self.check(TokenType::Var)
30974                    || self.peek().token_type.is_keyword()
30975                    || self.check(TokenType::String)
30976                    || self.check(TokenType::Number))
30977                    && (self.check_next(TokenType::Comma)
30978                        || self.check_next(TokenType::LParen)
30979                        || self.check_next(TokenType::Var)
30980                        || self.check_next(TokenType::Identifier))
30981                {
30982                    let args = self.parse_function_arguments()?;
30983                    self.expect(TokenType::RParen)?;
30984                    return Ok(Some(Expression::Function(Box::new(Function {
30985                        name: name.to_string(),
30986                        args,
30987                        distinct: false,
30988                        trailing_comments: Vec::new(),
30989                        use_bracket_syntax: false,
30990                        no_parens: false,
30991                        quoted: false,
30992                        span: None,
30993                        inferred_type: None,
30994                    }))));
30995                }
30996
30997                if self.check(TokenType::String) {
30998                    let args = self.parse_expression_list()?;
30999                    self.expect(TokenType::RParen)?;
31000                    return Ok(Some(Expression::Function(Box::new(Function {
31001                        name: name.to_string(),
31002                        args,
31003                        distinct: false,
31004                        trailing_comments: Vec::new(),
31005                        use_bracket_syntax: false,
31006                        no_parens: false,
31007                        quoted: false,
31008                        span: None,
31009                        inferred_type: None,
31010                    }))));
31011                }
31012
31013                let field = self.parse_datetime_field()?;
31014                if !self.match_token(TokenType::From) && !self.match_token(TokenType::Comma) {
31015                    return Err(self.parse_error("Expected FROM or comma after EXTRACT field"));
31016                }
31017                let this = self.parse_expression()?;
31018                let this = self.try_clickhouse_func_arg_alias(this);
31019                self.expect(TokenType::RParen)?;
31020                Ok(Some(Expression::Extract(Box::new(ExtractFunc {
31021                    this,
31022                    field,
31023                }))))
31024            }
31025            (crate::function_registry::TypedParseKind::Variadic, "STRUCT") => {
31026                let args = if self.check(TokenType::RParen) {
31027                    Vec::new()
31028                } else {
31029                    self.parse_struct_args()?
31030                };
31031                self.expect(TokenType::RParen)?;
31032                Ok(Some(Expression::Function(Box::new(Function {
31033                    name: name.to_string(),
31034                    args,
31035                    distinct: false,
31036                    trailing_comments: Vec::new(),
31037                    use_bracket_syntax: false,
31038                    no_parens: false,
31039                    quoted: false,
31040                    span: None,
31041                    inferred_type: None,
31042                }))))
31043            }
31044            (crate::function_registry::TypedParseKind::Variadic, "CHAR") => {
31045                let args = self.parse_expression_list()?;
31046                let charset = if self.match_token(TokenType::Using) {
31047                    if !self.is_at_end() {
31048                        let charset_token = self.advance();
31049                        Some(charset_token.text.clone())
31050                    } else {
31051                        None
31052                    }
31053                } else {
31054                    None
31055                };
31056                self.expect(TokenType::RParen)?;
31057                if charset.is_some() {
31058                    Ok(Some(Expression::CharFunc(Box::new(
31059                        crate::expressions::CharFunc {
31060                            args,
31061                            charset,
31062                            name: None,
31063                        },
31064                    ))))
31065                } else {
31066                    Ok(Some(Expression::Function(Box::new(Function {
31067                        name: name.to_string(),
31068                        args,
31069                        distinct: false,
31070                        trailing_comments: Vec::new(),
31071                        use_bracket_syntax: false,
31072                        no_parens: false,
31073                        quoted: false,
31074                        span: None,
31075                        inferred_type: None,
31076                    }))))
31077                }
31078            }
31079            (crate::function_registry::TypedParseKind::Variadic, "CHR") => {
31080                let args = self.parse_expression_list()?;
31081                let charset = if self.match_token(TokenType::Using) {
31082                    if !self.is_at_end() {
31083                        let charset_token = self.advance();
31084                        Some(charset_token.text.clone())
31085                    } else {
31086                        None
31087                    }
31088                } else {
31089                    None
31090                };
31091                self.expect(TokenType::RParen)?;
31092                if charset.is_some() {
31093                    Ok(Some(Expression::CharFunc(Box::new(
31094                        crate::expressions::CharFunc {
31095                            args,
31096                            charset,
31097                            name: Some("CHR".to_string()),
31098                        },
31099                    ))))
31100                } else {
31101                    Ok(Some(Expression::Function(Box::new(Function {
31102                        name: name.to_string(),
31103                        args,
31104                        distinct: false,
31105                        trailing_comments: Vec::new(),
31106                        use_bracket_syntax: false,
31107                        no_parens: false,
31108                        quoted: false,
31109                        span: None,
31110                        inferred_type: None,
31111                    }))))
31112                }
31113            }
31114            (crate::function_registry::TypedParseKind::Variadic, "RANGE_N") => {
31115                let this = self.parse_bitwise_or()?;
31116                self.expect(TokenType::Between)?;
31117                let mut expressions = Vec::new();
31118                while !self.check(TokenType::Each) && !self.check(TokenType::RParen) {
31119                    expressions.push(self.parse_expression()?);
31120                    if !self.match_token(TokenType::Comma) {
31121                        break;
31122                    }
31123                }
31124                let each = if self.match_token(TokenType::Each) {
31125                    Some(Box::new(self.parse_expression()?))
31126                } else {
31127                    None
31128                };
31129                self.expect(TokenType::RParen)?;
31130                Ok(Some(Expression::RangeN(Box::new(RangeN {
31131                    this: Box::new(this),
31132                    expressions,
31133                    each,
31134                }))))
31135            }
31136            (crate::function_registry::TypedParseKind::Variadic, "XMLTABLE") => {
31137                if let Some(xml_table) = self.parse_xml_table()? {
31138                    self.expect(TokenType::RParen)?;
31139                    Ok(Some(xml_table))
31140                } else {
31141                    Err(self.parse_error("Failed to parse XMLTABLE"))
31142                }
31143            }
31144            (crate::function_registry::TypedParseKind::Variadic, "XMLELEMENT") => {
31145                if let Some(elem) = self.parse_xml_element()? {
31146                    self.expect(TokenType::RParen)?;
31147                    Ok(Some(elem))
31148                } else {
31149                    self.expect(TokenType::RParen)?;
31150                    Ok(Some(Expression::Function(Box::new(Function {
31151                        name: name.to_string(),
31152                        args: Vec::new(),
31153                        distinct: false,
31154                        trailing_comments: Vec::new(),
31155                        use_bracket_syntax: false,
31156                        no_parens: false,
31157                        quoted: false,
31158                        span: None,
31159                        inferred_type: None,
31160                    }))))
31161                }
31162            }
31163            (crate::function_registry::TypedParseKind::Variadic, "XMLATTRIBUTES") => {
31164                let mut attrs = Vec::new();
31165                if !self.check(TokenType::RParen) {
31166                    loop {
31167                        let expr = self.parse_expression()?;
31168                        if self.match_token(TokenType::As) {
31169                            let alias_ident = self.expect_identifier_or_keyword_with_quoted()?;
31170                            attrs.push(Expression::Alias(Box::new(Alias {
31171                                this: expr,
31172                                alias: alias_ident,
31173                                column_aliases: Vec::new(),
31174                                pre_alias_comments: Vec::new(),
31175                                trailing_comments: Vec::new(),
31176                                inferred_type: None,
31177                            })));
31178                        } else {
31179                            attrs.push(expr);
31180                        }
31181                        if !self.match_token(TokenType::Comma) {
31182                            break;
31183                        }
31184                    }
31185                }
31186                self.expect(TokenType::RParen)?;
31187                Ok(Some(Expression::Function(Box::new(Function {
31188                    name: "XMLATTRIBUTES".to_string(),
31189                    args: attrs,
31190                    distinct: false,
31191                    trailing_comments: Vec::new(),
31192                    use_bracket_syntax: false,
31193                    no_parens: false,
31194                    quoted: false,
31195                    span: None,
31196                    inferred_type: None,
31197                }))))
31198            }
31199            (crate::function_registry::TypedParseKind::Variadic, "XMLCOMMENT") => {
31200                let args = if self.check(TokenType::RParen) {
31201                    Vec::new()
31202                } else {
31203                    self.parse_expression_list()?
31204                };
31205                self.expect(TokenType::RParen)?;
31206                Ok(Some(Expression::Function(Box::new(Function {
31207                    name: "XMLCOMMENT".to_string(),
31208                    args,
31209                    distinct: false,
31210                    trailing_comments: Vec::new(),
31211                    use_bracket_syntax: false,
31212                    no_parens: false,
31213                    quoted: false,
31214                    span: None,
31215                    inferred_type: None,
31216                }))))
31217            }
31218            (crate::function_registry::TypedParseKind::Variadic, "MATCH") => {
31219                let expressions = if self.check(TokenType::Table)
31220                    && !matches!(
31221                        self.config.dialect,
31222                        Some(crate::dialects::DialectType::ClickHouse)
31223                    ) {
31224                    self.skip();
31225                    let table_name = self.expect_identifier_or_keyword()?;
31226                    vec![Expression::Var(Box::new(Var {
31227                        this: format!("TABLE {}", table_name),
31228                    }))]
31229                } else {
31230                    self.parse_expression_list()?
31231                };
31232
31233                self.expect(TokenType::RParen)?;
31234
31235                if !self.check_keyword_text("AGAINST") {
31236                    return Ok(Some(Expression::Function(Box::new(Function {
31237                        name: "MATCH".to_string(),
31238                        args: expressions,
31239                        distinct: false,
31240                        trailing_comments: Vec::new(),
31241                        use_bracket_syntax: false,
31242                        no_parens: false,
31243                        quoted: false,
31244                        span: None,
31245                        inferred_type: None,
31246                    }))));
31247                }
31248
31249                self.skip();
31250                self.expect(TokenType::LParen)?;
31251                let search_expr = self.parse_primary()?;
31252
31253                let modifier = if self.match_text_seq(&["IN", "NATURAL", "LANGUAGE", "MODE"]) {
31254                    if self.match_text_seq(&["WITH", "QUERY", "EXPANSION"]) {
31255                        Some(Box::new(Expression::Var(Box::new(Var {
31256                            this: "IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION".to_string(),
31257                        }))))
31258                    } else {
31259                        Some(Box::new(Expression::Var(Box::new(Var {
31260                            this: "IN NATURAL LANGUAGE MODE".to_string(),
31261                        }))))
31262                    }
31263                } else if self.match_text_seq(&["IN", "BOOLEAN", "MODE"]) {
31264                    Some(Box::new(Expression::Var(Box::new(Var {
31265                        this: "IN BOOLEAN MODE".to_string(),
31266                    }))))
31267                } else if self.match_text_seq(&["WITH", "QUERY", "EXPANSION"]) {
31268                    Some(Box::new(Expression::Var(Box::new(Var {
31269                        this: "WITH QUERY EXPANSION".to_string(),
31270                    }))))
31271                } else {
31272                    None
31273                };
31274
31275                self.expect(TokenType::RParen)?;
31276
31277                Ok(Some(Expression::MatchAgainst(Box::new(MatchAgainst {
31278                    this: Box::new(search_expr),
31279                    expressions,
31280                    modifier,
31281                }))))
31282            }
31283            (crate::function_registry::TypedParseKind::Variadic, "TRANSFORM") => {
31284                let expressions = if self.check(TokenType::RParen) {
31285                    Vec::new()
31286                } else {
31287                    self.parse_function_args_with_lambda()?
31288                };
31289                self.expect(TokenType::RParen)?;
31290
31291                let row_format_before = if self.match_token(TokenType::Row) {
31292                    self.parse_row()?
31293                } else {
31294                    None
31295                };
31296
31297                let record_writer = if self.match_text_seq(&["RECORDWRITER"]) {
31298                    Some(Box::new(self.parse_expression()?))
31299                } else {
31300                    None
31301                };
31302
31303                if self.match_token(TokenType::Using) {
31304                    let command_script = Some(Box::new(self.parse_expression()?));
31305                    let schema = if self.match_token(TokenType::As) {
31306                        self.parse_schema()?
31307                    } else {
31308                        None
31309                    };
31310
31311                    let row_format_after = if self.match_token(TokenType::Row) {
31312                        self.parse_row()?
31313                    } else {
31314                        None
31315                    };
31316
31317                    let record_reader = if self.match_text_seq(&["RECORDREADER"]) {
31318                        Some(Box::new(self.parse_expression()?))
31319                    } else {
31320                        None
31321                    };
31322
31323                    Ok(Some(Expression::QueryTransform(Box::new(QueryTransform {
31324                        expressions,
31325                        command_script,
31326                        schema: schema.map(Box::new),
31327                        row_format_before: row_format_before.map(Box::new),
31328                        record_writer,
31329                        row_format_after: row_format_after.map(Box::new),
31330                        record_reader,
31331                    }))))
31332                } else {
31333                    Ok(Some(Expression::Function(Box::new(Function {
31334                        name: name.to_string(),
31335                        args: expressions,
31336                        distinct: false,
31337                        trailing_comments: Vec::new(),
31338                        use_bracket_syntax: false,
31339                        no_parens: false,
31340                        quoted,
31341                        span: None,
31342                        inferred_type: None,
31343                    }))))
31344                }
31345            }
31346            (crate::function_registry::TypedParseKind::Variadic, "CONVERT") => {
31347                let is_try = upper_name == "TRY_CONVERT";
31348                let is_tsql = matches!(
31349                    self.config.dialect,
31350                    Some(crate::dialects::DialectType::TSQL)
31351                        | Some(crate::dialects::DialectType::Fabric)
31352                );
31353
31354                if is_tsql {
31355                    let saved = self.current;
31356                    let orig_type_text = if self.current < self.tokens.len() {
31357                        self.tokens[self.current].text.to_ascii_uppercase()
31358                    } else {
31359                        String::new()
31360                    };
31361                    let dt = self.parse_data_type();
31362                    if let Ok(mut dt) = dt {
31363                        if self.match_token(TokenType::Comma) {
31364                            if orig_type_text == "NVARCHAR" || orig_type_text == "NCHAR" {
31365                                dt = match dt {
31366                                    crate::expressions::DataType::VarChar { length, .. } => {
31367                                        if let Some(len) = length {
31368                                            crate::expressions::DataType::Custom {
31369                                                name: format!("{}({})", orig_type_text, len),
31370                                            }
31371                                        } else {
31372                                            crate::expressions::DataType::Custom {
31373                                                name: orig_type_text.clone(),
31374                                            }
31375                                        }
31376                                    }
31377                                    crate::expressions::DataType::Char { length } => {
31378                                        if let Some(len) = length {
31379                                            crate::expressions::DataType::Custom {
31380                                                name: format!("{}({})", orig_type_text, len),
31381                                            }
31382                                        } else {
31383                                            crate::expressions::DataType::Custom {
31384                                                name: orig_type_text.clone(),
31385                                            }
31386                                        }
31387                                    }
31388                                    other => other,
31389                                };
31390                            }
31391                            let value = self.parse_expression()?;
31392                            let style = if self.match_token(TokenType::Comma) {
31393                                Some(self.parse_expression()?)
31394                            } else {
31395                                None
31396                            };
31397                            self.expect(TokenType::RParen)?;
31398                            let func_name = if is_try { "TRY_CONVERT" } else { "CONVERT" };
31399                            let mut args = vec![Expression::DataType(dt), value];
31400                            if let Some(s) = style {
31401                                args.push(s);
31402                            }
31403                            return Ok(Some(Expression::Function(Box::new(Function {
31404                                name: func_name.to_string(),
31405                                args,
31406                                distinct: false,
31407                                trailing_comments: Vec::new(),
31408                                use_bracket_syntax: false,
31409                                no_parens: false,
31410                                quoted: false,
31411                                span: None,
31412                                inferred_type: None,
31413                            }))));
31414                        }
31415                        self.current = saved;
31416                    } else {
31417                        self.current = saved;
31418                    }
31419                }
31420
31421                let this = self.parse_expression()?;
31422                if self.match_token(TokenType::Using) {
31423                    let charset = self.expect_identifier()?;
31424                    self.expect(TokenType::RParen)?;
31425                    Ok(Some(Expression::Cast(Box::new(Cast {
31426                        this,
31427                        to: DataType::CharacterSet { name: charset },
31428                        trailing_comments: Vec::new(),
31429                        double_colon_syntax: false,
31430                        format: None,
31431                        default: None,
31432                        inferred_type: None,
31433                    }))))
31434                } else if self.match_token(TokenType::Comma) {
31435                    let mut args = vec![this];
31436                    args.push(self.parse_expression()?);
31437                    while self.match_token(TokenType::Comma) {
31438                        args.push(self.parse_expression()?);
31439                    }
31440                    self.expect(TokenType::RParen)?;
31441                    let func_name = if is_try { "TRY_CONVERT" } else { "CONVERT" };
31442                    Ok(Some(Expression::Function(Box::new(Function {
31443                        name: func_name.to_string(),
31444                        args,
31445                        distinct: false,
31446                        trailing_comments: Vec::new(),
31447                        use_bracket_syntax: false,
31448                        no_parens: false,
31449                        quoted: false,
31450                        span: None,
31451                        inferred_type: None,
31452                    }))))
31453                } else {
31454                    self.expect(TokenType::RParen)?;
31455                    let func_name = if is_try { "TRY_CONVERT" } else { "CONVERT" };
31456                    Ok(Some(Expression::Function(Box::new(Function {
31457                        name: func_name.to_string(),
31458                        args: vec![this],
31459                        distinct: false,
31460                        trailing_comments: Vec::new(),
31461                        use_bracket_syntax: false,
31462                        no_parens: false,
31463                        quoted: false,
31464                        span: None,
31465                        inferred_type: None,
31466                    }))))
31467                }
31468            }
31469            (crate::function_registry::TypedParseKind::Variadic, "TRIM") => {
31470                let (position, position_explicit) = if self.match_token(TokenType::Leading) {
31471                    (TrimPosition::Leading, true)
31472                } else if self.match_token(TokenType::Trailing) {
31473                    (TrimPosition::Trailing, true)
31474                } else if self.match_token(TokenType::Both) {
31475                    (TrimPosition::Both, true)
31476                } else {
31477                    (TrimPosition::Both, false)
31478                };
31479
31480                if position_explicit || self.check(TokenType::From) {
31481                    if self.match_token(TokenType::From) {
31482                        let this = self.parse_expression()?;
31483                        self.expect(TokenType::RParen)?;
31484                        Ok(Some(Expression::Trim(Box::new(TrimFunc {
31485                            this,
31486                            characters: None,
31487                            position,
31488                            sql_standard_syntax: true,
31489                            position_explicit,
31490                        }))))
31491                    } else {
31492                        let first_expr = self.parse_bitwise_or()?;
31493                        let first_expr = self.try_clickhouse_func_arg_alias(first_expr);
31494                        if self.match_token(TokenType::From) {
31495                            let this = self.parse_bitwise_or()?;
31496                            let this = self.try_clickhouse_func_arg_alias(this);
31497                            self.expect(TokenType::RParen)?;
31498                            Ok(Some(Expression::Trim(Box::new(TrimFunc {
31499                                this,
31500                                characters: Some(first_expr),
31501                                position,
31502                                sql_standard_syntax: true,
31503                                position_explicit,
31504                            }))))
31505                        } else {
31506                            self.expect(TokenType::RParen)?;
31507                            Ok(Some(Expression::Trim(Box::new(TrimFunc {
31508                                this: first_expr,
31509                                characters: None,
31510                                position,
31511                                sql_standard_syntax: true,
31512                                position_explicit,
31513                            }))))
31514                        }
31515                    }
31516                } else {
31517                    let first_expr = self.parse_expression()?;
31518                    let first_expr = self.try_clickhouse_func_arg_alias(first_expr);
31519                    if self.match_token(TokenType::From) {
31520                        let this = self.parse_expression()?;
31521                        let this = self.try_clickhouse_func_arg_alias(this);
31522                        self.expect(TokenType::RParen)?;
31523                        Ok(Some(Expression::Trim(Box::new(TrimFunc {
31524                            this,
31525                            characters: Some(first_expr),
31526                            position: TrimPosition::Both,
31527                            sql_standard_syntax: true,
31528                            position_explicit: false,
31529                        }))))
31530                    } else if self.match_token(TokenType::Comma) {
31531                        let second_expr = self.parse_expression()?;
31532                        self.expect(TokenType::RParen)?;
31533                        let trim_pattern_first = matches!(
31534                            self.config.dialect,
31535                            Some(crate::dialects::DialectType::Spark)
31536                        );
31537                        let (this, characters) = if trim_pattern_first {
31538                            (second_expr, first_expr)
31539                        } else {
31540                            (first_expr, second_expr)
31541                        };
31542                        Ok(Some(Expression::Trim(Box::new(TrimFunc {
31543                            this,
31544                            characters: Some(characters),
31545                            position: TrimPosition::Both,
31546                            sql_standard_syntax: false,
31547                            position_explicit: false,
31548                        }))))
31549                    } else {
31550                        self.expect(TokenType::RParen)?;
31551                        Ok(Some(Expression::Trim(Box::new(TrimFunc {
31552                            this: first_expr,
31553                            characters: None,
31554                            position: TrimPosition::Both,
31555                            sql_standard_syntax: false,
31556                            position_explicit: false,
31557                        }))))
31558                    }
31559                }
31560            }
31561            (crate::function_registry::TypedParseKind::Variadic, "OVERLAY") => {
31562                if matches!(
31563                    self.config.dialect,
31564                    Some(crate::dialects::DialectType::ClickHouse)
31565                ) {
31566                    let args = self.parse_function_arguments()?;
31567                    self.expect(TokenType::RParen)?;
31568                    return Ok(Some(Expression::Function(Box::new(Function {
31569                        name: name.to_string(),
31570                        args,
31571                        distinct: false,
31572                        trailing_comments: Vec::new(),
31573                        use_bracket_syntax: false,
31574                        no_parens: false,
31575                        quoted: false,
31576                        span: None,
31577                        inferred_type: None,
31578                    }))));
31579                }
31580
31581                let this = self.parse_expression()?;
31582                if self.match_token(TokenType::Placing) {
31583                    let replacement = self.parse_expression()?;
31584                    self.expect(TokenType::From)?;
31585                    let from = self.parse_expression()?;
31586                    let length = if self.match_token(TokenType::For) {
31587                        Some(self.parse_expression()?)
31588                    } else {
31589                        None
31590                    };
31591                    self.expect(TokenType::RParen)?;
31592                    Ok(Some(Expression::Overlay(Box::new(OverlayFunc {
31593                        this,
31594                        replacement,
31595                        from,
31596                        length,
31597                    }))))
31598                } else if self.match_token(TokenType::Comma) {
31599                    let replacement = self.parse_expression()?;
31600                    if self.match_token(TokenType::Comma) {
31601                        let from = self.parse_expression()?;
31602                        let length = if self.match_token(TokenType::Comma) {
31603                            Some(self.parse_expression()?)
31604                        } else {
31605                            None
31606                        };
31607                        self.expect(TokenType::RParen)?;
31608                        Ok(Some(Expression::Overlay(Box::new(OverlayFunc {
31609                            this,
31610                            replacement,
31611                            from,
31612                            length,
31613                        }))))
31614                    } else {
31615                        self.expect(TokenType::RParen)?;
31616                        Ok(Some(Expression::Function(Box::new(Function {
31617                            name: name.to_string(),
31618                            args: vec![this, replacement],
31619                            distinct: false,
31620                            trailing_comments: Vec::new(),
31621                            use_bracket_syntax: false,
31622                            no_parens: false,
31623                            quoted: false,
31624                            span: None,
31625                            inferred_type: None,
31626                        }))))
31627                    }
31628                } else {
31629                    self.expect(TokenType::RParen)?;
31630                    Ok(Some(Expression::Function(Box::new(Function {
31631                        name: name.to_string(),
31632                        args: vec![this],
31633                        distinct: false,
31634                        trailing_comments: Vec::new(),
31635                        use_bracket_syntax: false,
31636                        no_parens: false,
31637                        quoted: false,
31638                        span: None,
31639                        inferred_type: None,
31640                    }))))
31641                }
31642            }
31643            (crate::function_registry::TypedParseKind::Variadic, "CEIL") => {
31644                let this = self.parse_expression()?;
31645                // Check for TO unit syntax (Druid: CEIL(__time TO WEEK))
31646                let to = if self.match_token(TokenType::To) {
31647                    // Parse the time unit as a variable/identifier
31648                    self.parse_var()?
31649                } else {
31650                    None
31651                };
31652                let decimals = if to.is_none() && self.match_token(TokenType::Comma) {
31653                    Some(self.parse_expression()?)
31654                } else {
31655                    None
31656                };
31657                self.expect(TokenType::RParen)?;
31658                Ok(Some(Expression::Ceil(Box::new(CeilFunc {
31659                    this,
31660                    decimals,
31661                    to,
31662                }))))
31663            }
31664            (crate::function_registry::TypedParseKind::Variadic, "TIMESTAMP_FROM_PARTS")
31665            | (crate::function_registry::TypedParseKind::Variadic, "TIMESTAMP_NTZ_FROM_PARTS")
31666            | (crate::function_registry::TypedParseKind::Variadic, "TIMESTAMP_LTZ_FROM_PARTS")
31667            | (crate::function_registry::TypedParseKind::Variadic, "TIMESTAMP_TZ_FROM_PARTS")
31668            | (crate::function_registry::TypedParseKind::Variadic, "DATE_FROM_PARTS")
31669            | (crate::function_registry::TypedParseKind::Variadic, "TIME_FROM_PARTS") => {
31670                let args = self.parse_expression_list()?;
31671                self.expect(TokenType::RParen)?;
31672                Ok(Some(Expression::Function(Box::new(Function {
31673                    name: name.to_string(),
31674                    args,
31675                    distinct: false,
31676                    trailing_comments: Vec::new(),
31677                    use_bracket_syntax: false,
31678                    no_parens: false,
31679                    quoted: false,
31680                    span: None,
31681                    inferred_type: None,
31682                }))))
31683            }
31684            (crate::function_registry::TypedParseKind::CastLike, "TRY_CAST") => {
31685                let this = self.parse_expression()?;
31686                self.expect(TokenType::As)?;
31687                let to = self.parse_data_type()?;
31688                self.expect(TokenType::RParen)?;
31689                Ok(Some(Expression::TryCast(Box::new(Cast {
31690                    this,
31691                    to,
31692                    trailing_comments: Vec::new(),
31693                    double_colon_syntax: false,
31694                    format: None,
31695                    default: None,
31696                    inferred_type: None,
31697                }))))
31698            }
31699            (crate::function_registry::TypedParseKind::Conditional, "IF") => {
31700                // ClickHouse: if() with zero args is valid in test queries
31701                if self.check(TokenType::RParen) {
31702                    self.skip();
31703                    return Ok(Some(Expression::Function(Box::new(Function {
31704                        name: name.to_string(),
31705                        args: vec![],
31706                        distinct: false,
31707                        trailing_comments: Vec::new(),
31708                        use_bracket_syntax: false,
31709                        no_parens: false,
31710                        quoted: false,
31711                        span: None,
31712                        inferred_type: None,
31713                    }))));
31714                }
31715                let args = self.parse_expression_list()?;
31716                self.expect(TokenType::RParen)?;
31717                let expr = if args.len() == 3 {
31718                    Expression::IfFunc(Box::new(crate::expressions::IfFunc {
31719                        original_name: Some(upper_name.to_string()),
31720                        condition: args[0].clone(),
31721                        true_value: args[1].clone(),
31722                        false_value: Some(args[2].clone()),
31723                        inferred_type: None,
31724                    }))
31725                } else if args.len() == 2 {
31726                    // IF with 2 args: condition, true_value (no false_value)
31727                    Expression::IfFunc(Box::new(crate::expressions::IfFunc {
31728                        original_name: Some(upper_name.to_string()),
31729                        condition: args[0].clone(),
31730                        true_value: args[1].clone(),
31731                        false_value: None,
31732                        inferred_type: None,
31733                    }))
31734                } else {
31735                    return Err(self.parse_error("IF function requires 2 or 3 arguments"));
31736                };
31737                Ok(Some(expr))
31738            }
31739            _ => {
31740                self.try_parse_registry_grouped_typed_family(name, upper_name, canonical_upper_name)
31741            }
31742        }
31743    }
31744
31745    /// Route heavy typed-function families via registry metadata groups.
31746    fn try_parse_registry_grouped_typed_family(
31747        &mut self,
31748        name: &str,
31749        upper_name: &str,
31750        canonical_upper_name: &str,
31751    ) -> Result<Option<Expression>> {
31752        use crate::function_registry::TypedDispatchGroup;
31753
31754        match crate::function_registry::typed_dispatch_group_by_name_upper(canonical_upper_name) {
31755            Some(TypedDispatchGroup::AggregateFamily) => self
31756                .parse_typed_aggregate_family(name, upper_name, canonical_upper_name)
31757                .map(Some),
31758            Some(TypedDispatchGroup::WindowFamily) => self
31759                .parse_typed_window_family(name, upper_name, canonical_upper_name)
31760                .map(Some),
31761            Some(TypedDispatchGroup::JsonFamily) => self
31762                .parse_typed_json_family(name, upper_name, canonical_upper_name)
31763                .map(Some),
31764            Some(TypedDispatchGroup::TranslateTeradataFamily) => {
31765                if matches!(
31766                    self.config.dialect,
31767                    Some(crate::dialects::DialectType::Teradata)
31768                ) {
31769                    self.parse_typed_translate_teradata_family(
31770                        name,
31771                        upper_name,
31772                        canonical_upper_name,
31773                    )
31774                    .map(Some)
31775                } else {
31776                    Ok(None)
31777                }
31778            }
31779            None => Ok(None),
31780        }
31781    }
31782
31783    fn make_unquoted_function(name: &str, args: Vec<Expression>) -> Expression {
31784        Expression::Function(Box::new(Function {
31785            name: name.to_string(),
31786            args,
31787            distinct: false,
31788            trailing_comments: Vec::new(),
31789            use_bracket_syntax: false,
31790            no_parens: false,
31791            quoted: false,
31792            span: None,
31793            inferred_type: None,
31794        }))
31795    }
31796
31797    fn make_simple_aggregate(
31798        name: &str,
31799        args: Vec<Expression>,
31800        distinct: bool,
31801        filter: Option<Expression>,
31802    ) -> Expression {
31803        Expression::AggregateFunction(Box::new(AggregateFunction {
31804            name: name.to_string(),
31805            args,
31806            distinct,
31807            filter,
31808            order_by: Vec::new(),
31809            limit: None,
31810            ignore_nulls: None,
31811            inferred_type: None,
31812        }))
31813    }
31814
31815    /// Parse phase-3 typed-function slices that are straightforward pass-throughs.
31816    fn try_parse_phase3_typed_function(
31817        &mut self,
31818        name: &str,
31819        _upper_name: &str,
31820        canonical_upper_name: &str,
31821    ) -> Result<Option<Expression>> {
31822        let Some(behavior) =
31823            crate::function_registry::parser_dispatch_behavior_by_name_upper(canonical_upper_name)
31824        else {
31825            return Ok(None);
31826        };
31827
31828        match behavior {
31829            crate::function_registry::ParserDispatchBehavior::ExprListFunction => {
31830                let args = self.parse_expression_list()?;
31831                self.expect(TokenType::RParen)?;
31832                Ok(Some(Self::make_unquoted_function(name, args)))
31833            }
31834            crate::function_registry::ParserDispatchBehavior::OptionalExprListFunction => {
31835                let args = if self.check(TokenType::RParen) {
31836                    Vec::new()
31837                } else {
31838                    self.parse_expression_list()?
31839                };
31840                self.expect(TokenType::RParen)?;
31841                Ok(Some(Self::make_unquoted_function(name, args)))
31842            }
31843            crate::function_registry::ParserDispatchBehavior::FunctionArgumentsFunction => {
31844                let args = self.parse_function_arguments()?;
31845                self.expect(TokenType::RParen)?;
31846                Ok(Some(Self::make_unquoted_function(name, args)))
31847            }
31848            crate::function_registry::ParserDispatchBehavior::ZeroArgFunction => {
31849                self.expect(TokenType::RParen)?;
31850                Ok(Some(Self::make_unquoted_function(name, Vec::new())))
31851            }
31852            crate::function_registry::ParserDispatchBehavior::ExprListMaybeAggregateByFilter => {
31853                let args = if self.check(TokenType::RParen) {
31854                    Vec::new()
31855                } else {
31856                    self.parse_expression_list()?
31857                };
31858                self.expect(TokenType::RParen)?;
31859                let filter = self.parse_filter_clause()?;
31860                if filter.is_some() {
31861                    Ok(Some(Self::make_simple_aggregate(name, args, false, filter)))
31862                } else {
31863                    Ok(Some(Self::make_unquoted_function(name, args)))
31864                }
31865            }
31866            crate::function_registry::ParserDispatchBehavior::ExprListMaybeAggregateByAggSuffix => {
31867                let args = self.parse_expression_list()?;
31868                self.expect(TokenType::RParen)?;
31869                let filter = self.parse_filter_clause()?;
31870                if canonical_upper_name.ends_with("_AGG") || filter.is_some() {
31871                    Ok(Some(Self::make_simple_aggregate(name, args, false, filter)))
31872                } else {
31873                    Ok(Some(Self::make_unquoted_function(name, args)))
31874                }
31875            }
31876            crate::function_registry::ParserDispatchBehavior::HashLike => {
31877                let args = self.parse_expression_list()?;
31878                self.expect(TokenType::RParen)?;
31879                let filter = self.parse_filter_clause()?;
31880                if canonical_upper_name == "HASH_AGG" || filter.is_some() {
31881                    Ok(Some(Self::make_simple_aggregate(name, args, false, filter)))
31882                } else {
31883                    Ok(Some(Self::make_unquoted_function(name, args)))
31884                }
31885            }
31886            crate::function_registry::ParserDispatchBehavior::HllAggregate => {
31887                let distinct = self.match_token(TokenType::Distinct);
31888                let args = if self.match_token(TokenType::Star) {
31889                    vec![Expression::Star(Star {
31890                        table: None,
31891                        except: None,
31892                        replace: None,
31893                        rename: None,
31894                        trailing_comments: Vec::new(),
31895                        span: None,
31896                    })]
31897                } else if self.check(TokenType::RParen) {
31898                    Vec::new()
31899                } else {
31900                    self.parse_expression_list()?
31901                };
31902                self.expect(TokenType::RParen)?;
31903                let filter = self.parse_filter_clause()?;
31904                Ok(Some(Self::make_simple_aggregate(
31905                    name, args, distinct, filter,
31906                )))
31907            }
31908            crate::function_registry::ParserDispatchBehavior::PercentileAggregate => {
31909                let distinct = self.match_token(TokenType::Distinct);
31910                if !distinct {
31911                    self.match_token(TokenType::All);
31912                }
31913                let args = self.parse_expression_list()?;
31914                self.expect(TokenType::RParen)?;
31915                let filter = self.parse_filter_clause()?;
31916                Ok(Some(Self::make_simple_aggregate(
31917                    name, args, distinct, filter,
31918                )))
31919            }
31920            crate::function_registry::ParserDispatchBehavior::ExprListAggregate => {
31921                let args = self.parse_expression_list()?;
31922                self.expect(TokenType::RParen)?;
31923                let filter = self.parse_filter_clause()?;
31924                Ok(Some(Self::make_simple_aggregate(name, args, false, filter)))
31925            }
31926            crate::function_registry::ParserDispatchBehavior::UnaryAggregate => {
31927                let this = self.parse_expression()?;
31928                self.expect(TokenType::RParen)?;
31929                let filter = self.parse_filter_clause()?;
31930                Ok(Some(Self::make_simple_aggregate(
31931                    name,
31932                    vec![this],
31933                    false,
31934                    filter,
31935                )))
31936            }
31937            crate::function_registry::ParserDispatchBehavior::TranslateNonTeradata => {
31938                if matches!(
31939                    self.config.dialect,
31940                    Some(crate::dialects::DialectType::Teradata)
31941                ) {
31942                    return Ok(None);
31943                }
31944                let args = self.parse_expression_list()?;
31945                self.expect(TokenType::RParen)?;
31946                Ok(Some(Self::make_unquoted_function(name, args)))
31947            }
31948        }
31949    }
31950
31951    /// Parse a typed function call (after the opening paren)
31952    /// Following Python SQLGlot pattern: match all function aliases to typed expressions
31953    fn parse_typed_function(
31954        &mut self,
31955        name: &str,
31956        upper_name: &str,
31957        quoted: bool,
31958    ) -> Result<Expression> {
31959        let canonical_upper_name =
31960            crate::function_registry::canonical_typed_function_name_upper(upper_name);
31961
31962        // Handle internal function rewrites (sqlglot internal functions that map to CAST)
31963        if canonical_upper_name == "TIME_TO_TIME_STR" {
31964            let arg = self.parse_expression()?;
31965            self.expect(TokenType::RParen)?;
31966            return Ok(Expression::Cast(Box::new(Cast {
31967                this: arg,
31968                to: DataType::Text,
31969                trailing_comments: Vec::new(),
31970                double_colon_syntax: false,
31971                format: None,
31972                default: None,
31973                inferred_type: None,
31974            })));
31975        }
31976
31977        if let Some(expr) =
31978            self.try_parse_registry_typed_function(name, upper_name, canonical_upper_name, quoted)?
31979        {
31980            return Ok(expr);
31981        }
31982        if let Some(expr) =
31983            self.try_parse_phase3_typed_function(name, upper_name, canonical_upper_name)?
31984        {
31985            return Ok(expr);
31986        }
31987
31988        self.parse_generic_function(name, quoted)
31989    }
31990
31991    fn parse_typed_aggregate_family(
31992        &mut self,
31993        name: &str,
31994        upper_name: &str,
31995        canonical_upper_name: &str,
31996    ) -> Result<Expression> {
31997        match canonical_upper_name {
31998            // COUNT function
31999            "COUNT" => {
32000                let (this, star, distinct) = if self.check(TokenType::RParen) {
32001                    (None, false, false)
32002                } else if self.match_token(TokenType::Star) {
32003                    (None, true, false)
32004                } else if self.match_token(TokenType::All) {
32005                    // COUNT(ALL expr) - ALL is the default, just consume it
32006                    (Some(self.parse_expression()?), false, false)
32007                } else if self.match_token(TokenType::Distinct) {
32008                    let first_expr = self.parse_expression()?;
32009                    // Check for multiple columns: COUNT(DISTINCT a, b, c)
32010                    if self.match_token(TokenType::Comma) {
32011                        let mut args = vec![first_expr];
32012                        loop {
32013                            args.push(self.parse_expression()?);
32014                            if !self.match_token(TokenType::Comma) {
32015                                break;
32016                            }
32017                        }
32018                        // Return as a tuple expression for COUNT DISTINCT over multiple columns
32019                        (
32020                            Some(Expression::Tuple(Box::new(Tuple { expressions: args }))),
32021                            false,
32022                            true,
32023                        )
32024                    } else {
32025                        (Some(first_expr), false, true)
32026                    }
32027                } else {
32028                    let first_expr = self.parse_expression()?;
32029                    // ClickHouse: consume optional AS alias inside function args (e.g., count(NULL AS a))
32030                    let first_expr = if matches!(
32031                        self.config.dialect,
32032                        Some(crate::dialects::DialectType::ClickHouse)
32033                    ) && self.check(TokenType::As)
32034                    {
32035                        self.skip(); // consume AS
32036                        let alias = self.expect_identifier_or_keyword_with_quoted()?;
32037                        Expression::Alias(Box::new(Alias {
32038                            this: first_expr,
32039                            alias,
32040                            column_aliases: Vec::new(),
32041                            pre_alias_comments: Vec::new(),
32042                            trailing_comments: Vec::new(),
32043                            inferred_type: None,
32044                        }))
32045                    } else {
32046                        first_expr
32047                    };
32048                    // Check for multiple arguments (rare but possible)
32049                    if self.match_token(TokenType::Comma) {
32050                        let mut args = vec![first_expr];
32051                        loop {
32052                            args.push(self.parse_expression()?);
32053                            if !self.match_token(TokenType::Comma) {
32054                                break;
32055                            }
32056                        }
32057                        self.expect(TokenType::RParen)?;
32058                        // Multiple args without DISTINCT - treat as generic function
32059                        return Ok(Expression::Function(Box::new(Function {
32060                            name: name.to_string(),
32061                            args,
32062                            distinct: false,
32063                            trailing_comments: Vec::new(),
32064                            use_bracket_syntax: false,
32065                            no_parens: false,
32066                            quoted: false,
32067                            span: None,
32068                            inferred_type: None,
32069                        })));
32070                    }
32071                    (Some(first_expr), false, false)
32072                };
32073                // BigQuery: RESPECT NULLS / IGNORE NULLS inside COUNT
32074                let ignore_nulls = if self.match_token(TokenType::Ignore)
32075                    && self.match_token(TokenType::Nulls)
32076                {
32077                    Some(true)
32078                } else if self.match_token(TokenType::Respect) && self.match_token(TokenType::Nulls)
32079                {
32080                    Some(false)
32081                } else {
32082                    None
32083                };
32084                self.expect(TokenType::RParen)?;
32085                let filter = self.parse_filter_clause()?;
32086                // Also check for IGNORE NULLS / RESPECT NULLS after the closing paren
32087                let ignore_nulls = if ignore_nulls.is_some() {
32088                    ignore_nulls
32089                } else if self.match_keywords(&[TokenType::Ignore, TokenType::Nulls]) {
32090                    Some(true)
32091                } else if self.match_keywords(&[TokenType::Respect, TokenType::Nulls]) {
32092                    Some(false)
32093                } else {
32094                    None
32095                };
32096                Ok(Expression::Count(Box::new(CountFunc {
32097                    this,
32098                    star,
32099                    distinct,
32100                    filter,
32101                    ignore_nulls,
32102                    original_name: Some(name.to_string()),
32103                    inferred_type: None,
32104                })))
32105            }
32106
32107            // LIST function: LIST(SELECT ...) in Materialize - list constructor with subquery
32108            "LIST" => {
32109                let is_materialize = matches!(
32110                    self.config.dialect,
32111                    Some(crate::dialects::DialectType::Materialize)
32112                );
32113                if is_materialize && self.check(TokenType::Select) {
32114                    let query = self.parse_select()?;
32115                    self.expect(TokenType::RParen)?;
32116                    return Ok(Expression::List(Box::new(List {
32117                        expressions: vec![query],
32118                    })));
32119                }
32120                // For non-Materialize or non-subquery, parse as either generic function or aggregate.
32121                let distinct = self.match_token(TokenType::Distinct);
32122                let args = if self.check(TokenType::RParen) {
32123                    Vec::new()
32124                } else {
32125                    self.parse_function_arguments()?
32126                };
32127                let order_by = if self.match_token(TokenType::Order) {
32128                    self.expect(TokenType::By)?;
32129                    self.parse_order_by_list()?
32130                } else {
32131                    Vec::new()
32132                };
32133                let limit = if self.match_token(TokenType::Limit) {
32134                    Some(Box::new(self.parse_expression()?))
32135                } else {
32136                    None
32137                };
32138                self.expect(TokenType::RParen)?;
32139                let filter = self.parse_filter_clause()?;
32140
32141                if distinct || !order_by.is_empty() || limit.is_some() || filter.is_some() {
32142                    Ok(Expression::AggregateFunction(Box::new(AggregateFunction {
32143                        name: name.to_string(),
32144                        args,
32145                        distinct,
32146                        filter,
32147                        order_by,
32148                        limit,
32149                        ignore_nulls: None,
32150                        inferred_type: None,
32151                    })))
32152                } else {
32153                    Ok(Expression::Function(Box::new(Function {
32154                        name: name.to_string(),
32155                        args,
32156                        distinct: false,
32157                        trailing_comments: Vec::new(),
32158                        use_bracket_syntax: false,
32159                        no_parens: false,
32160                        quoted: false,
32161                        span: None,
32162                        inferred_type: None,
32163                    })))
32164                }
32165            }
32166
32167            // MAP function: MAP(SELECT ...) in Materialize - map constructor with subquery
32168            "MAP" => {
32169                let is_materialize = matches!(
32170                    self.config.dialect,
32171                    Some(crate::dialects::DialectType::Materialize)
32172                );
32173                if is_materialize && self.check(TokenType::Select) {
32174                    let query = self.parse_select()?;
32175                    self.expect(TokenType::RParen)?;
32176                    return Ok(Expression::ToMap(Box::new(ToMap {
32177                        this: Box::new(query),
32178                    })));
32179                }
32180                // For non-Materialize or non-subquery, fall through to generic handling
32181                let args = if self.check(TokenType::RParen) {
32182                    Vec::new()
32183                } else {
32184                    self.parse_function_arguments()?
32185                };
32186                self.expect(TokenType::RParen)?;
32187                Ok(Expression::Function(Box::new(Function {
32188                    name: name.to_string(),
32189                    args,
32190                    distinct: false,
32191                    trailing_comments: Vec::new(),
32192                    use_bracket_syntax: false,
32193                    no_parens: false,
32194                    quoted: false,
32195                    span: None,
32196                    inferred_type: None,
32197                })))
32198            }
32199
32200            // ARRAY function: ARRAY(SELECT ...) or ARRAY((SELECT ...) LIMIT n) is an array constructor with subquery
32201            // Different from ARRAY<type> which is a data type
32202            "ARRAY" => {
32203                // Check if this is ARRAY(SELECT ...) - array subquery constructor
32204                if self.check(TokenType::Select) {
32205                    let query = self.parse_select()?;
32206                    self.expect(TokenType::RParen)?;
32207                    // Pass the query directly as an argument to ARRAY function
32208                    // The generator will handle it correctly
32209                    return Ok(Expression::Function(Box::new(Function {
32210                        name: name.to_string(),
32211                        args: vec![query],
32212                        distinct: false,
32213                        trailing_comments: Vec::new(),
32214                        use_bracket_syntax: false,
32215                        no_parens: false,
32216                        quoted: false,
32217                        span: None,
32218                        inferred_type: None,
32219                    })));
32220                }
32221                // Check if this is ARRAY((SELECT ...) LIMIT n) - BigQuery allows LIMIT outside the subquery parens
32222                // This is common for constructs like ARRAY((SELECT AS STRUCT ...) LIMIT 10)
32223                if self.check(TokenType::LParen) {
32224                    // This could be a parenthesized subquery with modifiers after it
32225                    // Save position in case we need to backtrack
32226                    let saved_pos = self.current;
32227                    self.skip(); // consume opening paren
32228
32229                    // Check if there's a SELECT or WITH inside
32230                    if self.check(TokenType::Select) || self.check(TokenType::With) {
32231                        let inner_query = self.parse_statement()?;
32232                        self.expect(TokenType::RParen)?; // close inner parens
32233
32234                        // Now check for LIMIT/OFFSET modifiers outside the inner parens
32235                        let limit = if self.match_token(TokenType::Limit) {
32236                            let expr = self.parse_expression()?;
32237                            Some(Limit {
32238                                this: expr,
32239                                percent: false,
32240                                comments: Vec::new(),
32241                            })
32242                        } else {
32243                            None
32244                        };
32245
32246                        let offset = if self.match_token(TokenType::Offset) {
32247                            let expr = self.parse_expression()?;
32248                            let rows = if self.match_token(TokenType::Row)
32249                                || self.match_token(TokenType::Rows)
32250                            {
32251                                Some(true)
32252                            } else {
32253                                None
32254                            };
32255                            Some(Offset { this: expr, rows })
32256                        } else {
32257                            None
32258                        };
32259
32260                        self.expect(TokenType::RParen)?; // close ARRAY parens
32261
32262                        // Wrap the inner query in a Subquery with the modifiers
32263                        let subquery = Expression::Subquery(Box::new(Subquery {
32264                            this: inner_query,
32265                            alias: None,
32266                            column_aliases: Vec::new(),
32267                            order_by: None,
32268                            limit,
32269                            offset,
32270                            lateral: false,
32271                            modifiers_inside: false,
32272                            trailing_comments: Vec::new(),
32273                            distribute_by: None,
32274                            sort_by: None,
32275                            cluster_by: None,
32276                            inferred_type: None,
32277                        }));
32278
32279                        return Ok(Expression::Function(Box::new(Function {
32280                            name: name.to_string(),
32281                            args: vec![subquery],
32282                            distinct: false,
32283                            trailing_comments: Vec::new(),
32284                            use_bracket_syntax: false,
32285                            no_parens: false,
32286                            quoted: false,
32287                            span: None,
32288                            inferred_type: None,
32289                        })));
32290                    } else {
32291                        // Not a subquery, backtrack and parse as regular arguments
32292                        self.current = saved_pos;
32293                    }
32294                }
32295                // Otherwise fall through to parse as generic function or error
32296                // This could be ARRAY(...values...) or invalid syntax
32297                let args = if self.check(TokenType::RParen) {
32298                    Vec::new()
32299                } else {
32300                    self.parse_function_arguments()?
32301                };
32302                self.expect(TokenType::RParen)?;
32303                Ok(Expression::Function(Box::new(Function {
32304                    name: name.to_string(),
32305                    args,
32306                    distinct: false,
32307                    trailing_comments: Vec::new(),
32308                    use_bracket_syntax: false,
32309                    no_parens: false,
32310                    quoted: false,
32311                    span: None,
32312                    inferred_type: None,
32313                })))
32314            }
32315
32316            // Simple aggregate functions (SUM, AVG, MIN, MAX, etc.)
32317            // These can have multiple arguments in some contexts (e.g., MAX(a, b) is a scalar function)
32318            "SUM"
32319            | "AVG"
32320            | "MIN"
32321            | "MAX"
32322            | "ARRAY_AGG"
32323            | "ARRAY_CONCAT_AGG"
32324            | "STDDEV"
32325            | "STDDEV_POP"
32326            | "STDDEV_SAMP"
32327            | "VARIANCE"
32328            | "VAR_POP"
32329            | "VAR_SAMP"
32330            | "MEDIAN"
32331            | "MODE"
32332            | "FIRST"
32333            | "LAST"
32334            | "ANY_VALUE"
32335            | "APPROX_DISTINCT"
32336            | "APPROX_COUNT_DISTINCT"
32337            | "BIT_AND"
32338            | "BIT_OR"
32339            | "BIT_XOR" => {
32340                let distinct = if self.match_token(TokenType::Distinct) {
32341                    true
32342                } else {
32343                    self.match_token(TokenType::All); // ALL is the default, just consume it
32344                    false
32345                };
32346
32347                // MODE() can have zero arguments when used with WITHIN GROUP
32348                // e.g., MODE() WITHIN GROUP (ORDER BY col)
32349                if self.check(TokenType::RParen) {
32350                    // Empty args - will likely be followed by WITHIN GROUP
32351                    self.expect(TokenType::RParen)?;
32352                    let filter = self.parse_filter_clause()?;
32353                    let agg = AggFunc {
32354                        ignore_nulls: None,
32355                        this: Expression::Null(Null {}), // Placeholder for 0-arg aggregate
32356                        distinct: false,
32357                        filter,
32358                        order_by: Vec::new(),
32359                        having_max: None,
32360                        name: Some(name.to_string()),
32361                        limit: None,
32362                        inferred_type: None,
32363                    };
32364                    return Ok(match upper_name {
32365                        "MODE" => Expression::Mode(Box::new(agg)),
32366                        _ => {
32367                            // ClickHouse: allow zero-arg aggregates (server will validate)
32368                            if matches!(
32369                                self.config.dialect,
32370                                Some(crate::dialects::DialectType::ClickHouse)
32371                            ) {
32372                                Expression::Function(Box::new(Function {
32373                                    name: name.to_string(),
32374                                    args: Vec::new(),
32375                                    distinct: false,
32376                                    trailing_comments: Vec::new(),
32377                                    use_bracket_syntax: false,
32378                                    no_parens: false,
32379                                    quoted: false,
32380                                    span: None,
32381                                    inferred_type: None,
32382                                }))
32383                            } else {
32384                                return Err(self.parse_error(format!(
32385                                    "{} cannot have zero arguments",
32386                                    upper_name
32387                                )));
32388                            }
32389                        }
32390                    });
32391                }
32392
32393                let first_arg = self.parse_expression_with_clickhouse_alias()?;
32394
32395                // Check if there are more arguments (multi-arg scalar function like MAX(a, b))
32396                if self.match_token(TokenType::Comma) {
32397                    // Special handling for FIRST, LAST, ANY_VALUE with boolean second arg
32398                    // In Spark/Hive: first(col, true) means FIRST(col) IGNORE NULLS
32399                    let is_ignore_nulls_func = matches!(upper_name, "FIRST" | "LAST" | "ANY_VALUE");
32400
32401                    let second_arg = self.parse_expression()?;
32402
32403                    // Check if this is the IGNORE NULLS pattern: func(col, true)
32404                    if is_ignore_nulls_func && self.check(TokenType::RParen) {
32405                        if let Expression::Boolean(BooleanLiteral { value: true }) = &second_arg {
32406                            // This is func(col, true) -> FUNC(col) IGNORE NULLS
32407                            self.expect(TokenType::RParen)?;
32408                            let filter = self.parse_filter_clause()?;
32409                            let agg = AggFunc {
32410                                ignore_nulls: Some(true),
32411                                this: first_arg,
32412                                distinct,
32413                                filter,
32414                                order_by: Vec::new(),
32415                                having_max: None,
32416                                name: Some(name.to_string()),
32417                                limit: None,
32418                                inferred_type: None,
32419                            };
32420                            return Ok(match upper_name {
32421                                "FIRST" => Expression::First(Box::new(agg)),
32422                                "LAST" => Expression::Last(Box::new(agg)),
32423                                "ANY_VALUE" => Expression::AnyValue(Box::new(agg)),
32424                                _ => unreachable!(
32425                                    "function name already matched by is_ignore_nulls_func guard"
32426                                ),
32427                            });
32428                        }
32429                    }
32430
32431                    // Multiple arguments - treat as generic function call
32432                    let mut args = vec![first_arg, second_arg];
32433                    while self.match_token(TokenType::Comma) {
32434                        args.push(self.parse_expression()?);
32435                    }
32436                    self.expect(TokenType::RParen)?;
32437                    Ok(Expression::Function(Box::new(Function {
32438                        name: name.to_string(),
32439                        args,
32440                        distinct: false,
32441                        trailing_comments: Vec::new(),
32442                        use_bracket_syntax: false,
32443                        no_parens: false,
32444                        quoted: false,
32445                        span: None,
32446                        inferred_type: None,
32447                    })))
32448                } else {
32449                    // Check for IGNORE NULLS / RESPECT NULLS (BigQuery style)
32450                    let ignore_nulls = if self.match_token(TokenType::Ignore)
32451                        && self.match_token(TokenType::Nulls)
32452                    {
32453                        Some(true)
32454                    } else if self.match_token(TokenType::Respect)
32455                        && self.match_token(TokenType::Nulls)
32456                    {
32457                        Some(false)
32458                    } else {
32459                        None
32460                    };
32461
32462                    // Check for HAVING MAX/MIN inside aggregate (BigQuery syntax)
32463                    // e.g., ANY_VALUE(fruit HAVING MAX sold)
32464                    let having_max = if self.match_token(TokenType::Having) {
32465                        let is_max = if self.check_keyword_text("MAX") {
32466                            self.skip();
32467                            true
32468                        } else if self.check_keyword_text("MIN") {
32469                            self.skip();
32470                            false
32471                        } else {
32472                            return Err(
32473                                self.parse_error("Expected MAX or MIN after HAVING in aggregate")
32474                            );
32475                        };
32476                        let expr = self.parse_expression()?;
32477                        Some((Box::new(expr), is_max))
32478                    } else {
32479                        None
32480                    };
32481
32482                    // Check for ORDER BY inside aggregate (e.g., ARRAY_AGG(x ORDER BY y))
32483                    let order_by = if self.match_keywords(&[TokenType::Order, TokenType::By]) {
32484                        self.parse_order_by_list()?
32485                    } else {
32486                        Vec::new()
32487                    };
32488                    // Check for LIMIT inside aggregate (e.g., ARRAY_AGG(x ORDER BY y LIMIT 2))
32489                    // Also supports LIMIT offset, count (e.g., ARRAY_AGG(x ORDER BY y LIMIT 1, 10))
32490                    let limit = if self.match_token(TokenType::Limit) {
32491                        let first = self.parse_expression()?;
32492                        if self.match_token(TokenType::Comma) {
32493                            let second = self.parse_expression()?;
32494                            // Store as Tuple(offset, count)
32495                            Some(Box::new(Expression::Tuple(Box::new(Tuple {
32496                                expressions: vec![first, second],
32497                            }))))
32498                        } else {
32499                            Some(Box::new(first))
32500                        }
32501                    } else {
32502                        None
32503                    };
32504                    // Single argument - treat as aggregate function
32505                    self.expect(TokenType::RParen)?;
32506                    let filter = self.parse_filter_clause()?;
32507                    // Also check for IGNORE NULLS / RESPECT NULLS after the closing paren
32508                    // e.g., FIRST(col) IGNORE NULLS (Hive/Spark/generic SQL syntax)
32509                    let ignore_nulls = if ignore_nulls.is_some() {
32510                        ignore_nulls
32511                    } else if self.match_keywords(&[TokenType::Ignore, TokenType::Nulls]) {
32512                        Some(true)
32513                    } else if self.match_keywords(&[TokenType::Respect, TokenType::Nulls]) {
32514                        Some(false)
32515                    } else {
32516                        None
32517                    };
32518                    let agg = AggFunc {
32519                        ignore_nulls,
32520                        this: first_arg,
32521                        distinct,
32522                        filter,
32523                        order_by,
32524                        having_max,
32525                        name: Some(name.to_string()),
32526                        limit,
32527                        inferred_type: None,
32528                    };
32529                    Ok(match upper_name {
32530                        "SUM" => Expression::Sum(Box::new(agg)),
32531                        "AVG" => Expression::Avg(Box::new(agg)),
32532                        "MIN" => Expression::Min(Box::new(agg)),
32533                        "MAX" => Expression::Max(Box::new(agg)),
32534                        "ARRAY_AGG" => Expression::ArrayAgg(Box::new(agg)),
32535                        "ARRAY_CONCAT_AGG" => Expression::ArrayConcatAgg(Box::new(agg)),
32536                        "STDDEV" => Expression::Stddev(Box::new(agg)),
32537                        "STDDEV_POP" => Expression::StddevPop(Box::new(agg)),
32538                        "STDDEV_SAMP" => Expression::StddevSamp(Box::new(agg)),
32539                        "VARIANCE" => Expression::Variance(Box::new(agg)),
32540                        "VAR_POP" => Expression::VarPop(Box::new(agg)),
32541                        "VAR_SAMP" => Expression::VarSamp(Box::new(agg)),
32542                        "MEDIAN" => Expression::Median(Box::new(agg)),
32543                        "MODE" => Expression::Mode(Box::new(agg)),
32544                        "FIRST" => Expression::First(Box::new(agg)),
32545                        "LAST" => Expression::Last(Box::new(agg)),
32546                        "ANY_VALUE" => Expression::AnyValue(Box::new(agg)),
32547                        "APPROX_DISTINCT" => Expression::ApproxDistinct(Box::new(agg)),
32548                        "APPROX_COUNT_DISTINCT" => Expression::ApproxCountDistinct(Box::new(agg)),
32549                        "BIT_AND" => Expression::BitwiseAndAgg(Box::new(agg)),
32550                        "BIT_OR" => Expression::BitwiseOrAgg(Box::new(agg)),
32551                        "BIT_XOR" => Expression::BitwiseXorAgg(Box::new(agg)),
32552                        _ => unreachable!("aggregate function name already matched in caller"),
32553                    })
32554                }
32555            }
32556
32557            // STRING_AGG - STRING_AGG([DISTINCT] expr [, separator] [ORDER BY order_list])
32558            "STRING_AGG" => {
32559                let distinct = self.match_token(TokenType::Distinct);
32560                let this = self.parse_expression()?;
32561                // Separator is optional
32562                let separator = if self.match_token(TokenType::Comma) {
32563                    Some(self.parse_expression()?)
32564                } else {
32565                    None
32566                };
32567                let order_by = if self.match_keywords(&[TokenType::Order, TokenType::By]) {
32568                    Some(self.parse_order_by_list()?)
32569                } else {
32570                    None
32571                };
32572                // BigQuery: LIMIT inside STRING_AGG
32573                let limit = if self.match_token(TokenType::Limit) {
32574                    Some(Box::new(self.parse_expression()?))
32575                } else {
32576                    None
32577                };
32578                self.expect(TokenType::RParen)?;
32579                let filter = self.parse_filter_clause()?;
32580                Ok(Expression::StringAgg(Box::new(StringAggFunc {
32581                    this,
32582                    separator,
32583                    order_by,
32584                    distinct,
32585                    filter,
32586                    limit,
32587                    inferred_type: None,
32588                })))
32589            }
32590
32591            // GROUP_CONCAT - GROUP_CONCAT([DISTINCT] expr [, expr...] [ORDER BY order_list] [SEPARATOR 'sep'])
32592            // MySQL allows multiple args which get wrapped in CONCAT:
32593            // GROUP_CONCAT(a, b, c SEPARATOR ',') -> GroupConcat(CONCAT(a, b, c), SEPARATOR=',')
32594            "GROUP_CONCAT" => {
32595                let distinct = self.match_token(TokenType::Distinct);
32596                let first = self.parse_expression()?;
32597                // Check for additional comma-separated expressions (before ORDER BY or SEPARATOR)
32598                let mut exprs = vec![first];
32599                while self.match_token(TokenType::Comma) {
32600                    // Check if the next tokens are ORDER BY or SEPARATOR
32601                    // If so, the comma was part of the separator syntax (not more args)
32602                    if self.check(TokenType::Order) || self.check(TokenType::Separator) {
32603                        // This shouldn't happen normally in valid SQL, backtrack
32604                        break;
32605                    }
32606                    exprs.push(self.parse_expression()?);
32607                }
32608                // If multiple expressions, wrap in CONCAT (matches Python sqlglot behavior)
32609                let this = if exprs.len() == 1 {
32610                    exprs.pop().unwrap()
32611                } else {
32612                    Expression::Function(Box::new(Function::new("CONCAT".to_string(), exprs)))
32613                };
32614                // Parse optional ORDER BY
32615                let order_by = if self.match_keywords(&[TokenType::Order, TokenType::By]) {
32616                    Some(self.parse_order_by_list()?)
32617                } else {
32618                    None
32619                };
32620                // Parse optional SEPARATOR - can be a string literal or expression (e.g., variable)
32621                let separator = if self.match_token(TokenType::Separator) {
32622                    Some(self.parse_expression()?)
32623                } else {
32624                    None
32625                };
32626                self.expect(TokenType::RParen)?;
32627                let filter = self.parse_filter_clause()?;
32628                Ok(Expression::GroupConcat(Box::new(GroupConcatFunc {
32629                    this,
32630                    separator,
32631                    order_by,
32632                    distinct,
32633                    filter,
32634                    inferred_type: None,
32635                })))
32636            }
32637
32638            // LISTAGG - LISTAGG([DISTINCT] expr [, separator [ON OVERFLOW ...]]) WITHIN GROUP (ORDER BY ...)
32639            "LISTAGG" => {
32640                // Check for optional DISTINCT
32641                let distinct = self.match_token(TokenType::Distinct);
32642                let this = self.parse_expression()?;
32643                let separator = if self.match_token(TokenType::Comma) {
32644                    Some(self.parse_expression()?)
32645                } else {
32646                    None
32647                };
32648                // Parse optional ON OVERFLOW clause
32649                let on_overflow = if self.match_token(TokenType::On) {
32650                    if self.match_identifier("OVERFLOW") {
32651                        if self.match_identifier("ERROR") {
32652                            Some(ListAggOverflow::Error)
32653                        } else if self.match_token(TokenType::Truncate) {
32654                            // Optional filler string
32655                            let filler = if self.check(TokenType::String) {
32656                                Some(self.parse_expression()?)
32657                            } else {
32658                                None
32659                            };
32660                            // WITH COUNT or WITHOUT COUNT
32661                            let with_count = if self.match_token(TokenType::With) {
32662                                self.match_identifier("COUNT");
32663                                true
32664                            } else if self.match_identifier("WITHOUT") {
32665                                self.match_identifier("COUNT");
32666                                false
32667                            } else {
32668                                true // default is WITH COUNT
32669                            };
32670                            Some(ListAggOverflow::Truncate { filler, with_count })
32671                        } else {
32672                            None
32673                        }
32674                    } else {
32675                        None
32676                    }
32677                } else {
32678                    None
32679                };
32680                self.expect(TokenType::RParen)?;
32681                // WITHIN GROUP (ORDER BY ...) is handled by maybe_parse_over
32682                Ok(Expression::ListAgg(Box::new(ListAggFunc {
32683                    this,
32684                    separator,
32685                    on_overflow,
32686                    order_by: None,
32687                    distinct,
32688                    filter: None,
32689                    inferred_type: None,
32690                })))
32691            }
32692            _ => unreachable!(
32693                "phase-6 aggregate parser called with non-aggregate family name '{}'",
32694                canonical_upper_name
32695            ),
32696        }
32697    }
32698
32699    fn parse_typed_window_family(
32700        &mut self,
32701        name: &str,
32702        upper_name: &str,
32703        canonical_upper_name: &str,
32704    ) -> Result<Expression> {
32705        match canonical_upper_name {
32706            // Window functions with no arguments (ClickHouse allows args in row_number)
32707            "ROW_NUMBER" => {
32708                if self.check(TokenType::RParen) {
32709                    self.skip();
32710                    Ok(Expression::RowNumber(RowNumber))
32711                } else {
32712                    // ClickHouse: row_number(column1) — parse as regular function
32713                    let args = self.parse_function_args_list()?;
32714                    self.expect(TokenType::RParen)?;
32715                    let trailing_comments = self.previous_trailing_comments().to_vec();
32716                    Ok(Expression::Function(Box::new(Function {
32717                        name: name.to_string(),
32718                        args,
32719                        distinct: false,
32720                        trailing_comments,
32721                        use_bracket_syntax: false,
32722                        no_parens: false,
32723                        quoted: false,
32724                        span: None,
32725                        inferred_type: None,
32726                    })))
32727                }
32728            }
32729            "RANK" => {
32730                // DuckDB allows: RANK(ORDER BY col) OVER (...)
32731                // Oracle allows: RANK(val1, val2, ...) WITHIN GROUP (ORDER BY ...)
32732                let (order_by, args) = if self.check(TokenType::RParen) {
32733                    // RANK() - no arguments
32734                    (None, Vec::new())
32735                } else if self.match_token(TokenType::Order) {
32736                    // DuckDB: RANK(ORDER BY col)
32737                    self.expect(TokenType::By)?;
32738                    (Some(self.parse_order_by()?.expressions), Vec::new())
32739                } else {
32740                    // Oracle hypothetical: RANK(val1, val2, ...)
32741                    let mut args = vec![self.parse_expression()?];
32742                    while self.match_token(TokenType::Comma) {
32743                        args.push(self.parse_expression()?);
32744                    }
32745                    (None, args)
32746                };
32747                self.expect(TokenType::RParen)?;
32748                Ok(Expression::Rank(Rank { order_by, args }))
32749            }
32750            "DENSE_RANK" => {
32751                // Oracle allows: DENSE_RANK(val1, val2, ...) WITHIN GROUP (ORDER BY ...)
32752                let args = if self.check(TokenType::RParen) {
32753                    Vec::new()
32754                } else {
32755                    let mut args = vec![self.parse_expression()?];
32756                    while self.match_token(TokenType::Comma) {
32757                        args.push(self.parse_expression()?);
32758                    }
32759                    args
32760                };
32761                self.expect(TokenType::RParen)?;
32762                Ok(Expression::DenseRank(DenseRank { args }))
32763            }
32764            "PERCENT_RANK" => {
32765                // DuckDB allows: PERCENT_RANK(ORDER BY col) OVER (...)
32766                // Oracle allows: PERCENT_RANK(val1, val2, ...) WITHIN GROUP (ORDER BY ...)
32767                let (order_by, args) = if self.check(TokenType::RParen) {
32768                    // PERCENT_RANK() - no arguments
32769                    (None, Vec::new())
32770                } else if self.match_token(TokenType::Order) {
32771                    // DuckDB: PERCENT_RANK(ORDER BY col)
32772                    self.expect(TokenType::By)?;
32773                    (Some(self.parse_order_by()?.expressions), Vec::new())
32774                } else {
32775                    // Oracle hypothetical: PERCENT_RANK(val1, val2, ...)
32776                    let mut args = vec![self.parse_expression()?];
32777                    while self.match_token(TokenType::Comma) {
32778                        args.push(self.parse_expression()?);
32779                    }
32780                    (None, args)
32781                };
32782                self.expect(TokenType::RParen)?;
32783                Ok(Expression::PercentRank(PercentRank { order_by, args }))
32784            }
32785            "CUME_DIST" => {
32786                // DuckDB allows: CUME_DIST(ORDER BY col) OVER (...)
32787                // Oracle allows: CUME_DIST(val1, val2, ...) WITHIN GROUP (ORDER BY ...)
32788                let (order_by, args) = if self.check(TokenType::RParen) {
32789                    // CUME_DIST() - no arguments
32790                    (None, Vec::new())
32791                } else if self.match_token(TokenType::Order) {
32792                    // DuckDB: CUME_DIST(ORDER BY col)
32793                    self.expect(TokenType::By)?;
32794                    (Some(self.parse_order_by()?.expressions), Vec::new())
32795                } else {
32796                    // Oracle hypothetical: CUME_DIST(val1, val2, ...)
32797                    let mut args = vec![self.parse_expression()?];
32798                    while self.match_token(TokenType::Comma) {
32799                        args.push(self.parse_expression()?);
32800                    }
32801                    (None, args)
32802                };
32803                self.expect(TokenType::RParen)?;
32804                Ok(Expression::CumeDist(CumeDist { order_by, args }))
32805            }
32806
32807            // NTILE
32808            "NTILE" => {
32809                // num_buckets is optional (Databricks allows NTILE() with no args)
32810                let num_buckets = if self.check(TokenType::RParen) {
32811                    None
32812                } else {
32813                    Some(self.parse_expression()?)
32814                };
32815
32816                // ClickHouse: NTILE can have extra args (e.g., ntile(3, 2)) — skip them
32817                while matches!(
32818                    self.config.dialect,
32819                    Some(crate::dialects::DialectType::ClickHouse)
32820                ) && self.match_token(TokenType::Comma)
32821                {
32822                    let _ = self.parse_expression()?;
32823                }
32824
32825                // DuckDB allows: NTILE(n ORDER BY col) OVER (...)
32826                let order_by = if self.match_token(TokenType::Order) {
32827                    self.expect(TokenType::By)?;
32828                    Some(self.parse_order_by()?.expressions)
32829                } else {
32830                    None
32831                };
32832                self.expect(TokenType::RParen)?;
32833                Ok(Expression::NTile(Box::new(NTileFunc {
32834                    num_buckets,
32835                    order_by,
32836                })))
32837            }
32838
32839            // LEAD / LAG
32840            "LEAD" | "LAG" => {
32841                let this = self.parse_expression()?;
32842                let (offset, default) = if self.match_token(TokenType::Comma) {
32843                    let off = self.parse_expression()?;
32844                    let def = if self.match_token(TokenType::Comma) {
32845                        Some(self.parse_expression()?)
32846                    } else {
32847                        None
32848                    };
32849                    (Some(off), def)
32850                } else {
32851                    (None, None)
32852                };
32853                self.expect(TokenType::RParen)?;
32854                // Check for IGNORE NULLS / RESPECT NULLS
32855                let ignore_nulls = if self.match_keywords(&[TokenType::Ignore, TokenType::Nulls]) {
32856                    Some(true)
32857                } else if self.match_keywords(&[TokenType::Respect, TokenType::Nulls]) {
32858                    Some(false)
32859                } else {
32860                    None
32861                };
32862                let func = LeadLagFunc {
32863                    this,
32864                    offset,
32865                    default,
32866                    ignore_nulls,
32867                };
32868                Ok(if upper_name == "LEAD" {
32869                    Expression::Lead(Box::new(func))
32870                } else {
32871                    Expression::Lag(Box::new(func))
32872                })
32873            }
32874
32875            // FIRST_VALUE / LAST_VALUE
32876            "FIRST_VALUE" | "LAST_VALUE" => {
32877                let this = self.parse_expression()?;
32878                // Check for IGNORE NULLS / RESPECT NULLS inside the parens
32879                let mut ignore_nulls_inside = if self.match_token(TokenType::Ignore)
32880                    && self.match_token(TokenType::Nulls)
32881                {
32882                    Some(true)
32883                } else if self.match_token(TokenType::Respect) && self.match_token(TokenType::Nulls)
32884                {
32885                    Some(false) // RESPECT NULLS explicitly sets to false
32886                } else {
32887                    None
32888                };
32889                // Spark/Hive: first_value(col, true) means FIRST_VALUE(col) IGNORE NULLS
32890                if ignore_nulls_inside.is_none() && self.match_token(TokenType::Comma) {
32891                    let second_arg = self.parse_expression()?;
32892                    if let Expression::Boolean(BooleanLiteral { value: true }) = &second_arg {
32893                        ignore_nulls_inside = Some(true);
32894                    }
32895                    // If second arg is not true, just ignore it (not standard)
32896                }
32897                self.expect(TokenType::RParen)?;
32898                // Also check for IGNORE NULLS / RESPECT NULLS after the parens (some dialects use this syntax)
32899                let ignore_nulls: Option<bool> = if ignore_nulls_inside.is_some() {
32900                    ignore_nulls_inside
32901                } else if self.match_keywords(&[TokenType::Ignore, TokenType::Nulls]) {
32902                    Some(true)
32903                } else if self.match_keywords(&[TokenType::Respect, TokenType::Nulls]) {
32904                    Some(false)
32905                } else {
32906                    None
32907                };
32908                let func = ValueFunc { this, ignore_nulls };
32909                Ok(if upper_name == "FIRST_VALUE" {
32910                    Expression::FirstValue(Box::new(func))
32911                } else {
32912                    Expression::LastValue(Box::new(func))
32913                })
32914            }
32915
32916            // NTH_VALUE
32917            "NTH_VALUE" => {
32918                let this = self.parse_expression()?;
32919                self.expect(TokenType::Comma)?;
32920                let offset = self.parse_expression()?;
32921                // Check for IGNORE NULLS / RESPECT NULLS inside the parens
32922                let ignore_nulls_inside = if self.match_token(TokenType::Ignore)
32923                    && self.match_token(TokenType::Nulls)
32924                {
32925                    Some(true)
32926                } else if self.match_token(TokenType::Respect) && self.match_token(TokenType::Nulls)
32927                {
32928                    Some(false)
32929                } else {
32930                    None
32931                };
32932                self.expect(TokenType::RParen)?;
32933                // Check for Snowflake FROM FIRST / FROM LAST after the parens
32934                let from_first = if self.match_keywords(&[TokenType::From, TokenType::First]) {
32935                    Some(true)
32936                } else if self.match_keywords(&[TokenType::From, TokenType::Last]) {
32937                    Some(false)
32938                } else {
32939                    None
32940                };
32941                // Also check for IGNORE NULLS / RESPECT NULLS after the parens (and after FROM FIRST/LAST)
32942                let ignore_nulls: Option<bool> = if ignore_nulls_inside.is_some() {
32943                    ignore_nulls_inside
32944                } else if self.match_keywords(&[TokenType::Ignore, TokenType::Nulls]) {
32945                    Some(true)
32946                } else if self.match_keywords(&[TokenType::Respect, TokenType::Nulls]) {
32947                    Some(false)
32948                } else {
32949                    None
32950                };
32951                Ok(Expression::NthValue(Box::new(NthValueFunc {
32952                    this,
32953                    offset,
32954                    ignore_nulls,
32955                    from_first,
32956                })))
32957            }
32958            _ => unreachable!(
32959                "phase-6 window parser called with non-window family name '{}'",
32960                canonical_upper_name
32961            ),
32962        }
32963    }
32964
32965    fn parse_typed_json_family(
32966        &mut self,
32967        name: &str,
32968        upper_name: &str,
32969        canonical_upper_name: &str,
32970    ) -> Result<Expression> {
32971        match canonical_upper_name {
32972            // JSON functions
32973            "JSON_EXTRACT" | "JSON_EXTRACT_SCALAR" | "JSON_QUERY" | "JSON_VALUE" => {
32974                let this = self.parse_expression()?;
32975                // Path is optional for some dialects (e.g., TSQL JSON_QUERY with 1 arg defaults to '$')
32976                let path = if self.match_token(TokenType::Comma) {
32977                    self.parse_expression()?
32978                } else {
32979                    // Default path is '$' when not provided
32980                    Expression::Literal(Literal::String("$".to_string()))
32981                };
32982
32983                // SQLite JSON_EXTRACT supports multiple paths - check for additional paths
32984                // If multiple paths, use generic Function instead of typed expression
32985                if self.check(TokenType::Comma)
32986                    && !self.check_identifier("WITH")
32987                    && !self.check_identifier("WITHOUT")
32988                    && !self.check_identifier("KEEP")
32989                    && !self.check_identifier("OMIT")
32990                    && !self.check_identifier("NULL")
32991                    && !self.check_identifier("ERROR")
32992                    && !self.check_identifier("EMPTY")
32993                    && !self.check(TokenType::Returning)
32994                {
32995                    let mut args = vec![this, path];
32996                    while self.match_token(TokenType::Comma) {
32997                        args.push(self.parse_expression()?);
32998                    }
32999                    self.expect(TokenType::RParen)?;
33000                    return Ok(Expression::Function(Box::new(Function {
33001                        name: name.to_string(),
33002                        args,
33003                        distinct: false,
33004                        trailing_comments: Vec::new(),
33005                        use_bracket_syntax: false,
33006                        no_parens: false,
33007                        quoted: false,
33008                        span: None,
33009                        inferred_type: None,
33010                    })));
33011                }
33012
33013                // Parse JSON_QUERY/JSON_VALUE options (Trino/Presto style)
33014                // Options: WITH/WITHOUT [CONDITIONAL|UNCONDITIONAL] [ARRAY] WRAPPER
33015                //          KEEP QUOTES / OMIT QUOTES [ON SCALAR STRING]
33016                //          NULL ON ERROR / ERROR ON ERROR / EMPTY ON ERROR
33017                //          RETURNING type
33018                let mut wrapper_option: Option<String> = None;
33019                let mut quotes_option: Option<String> = None;
33020                let mut on_scalar_string = false;
33021                let mut on_error: Option<String> = None;
33022                let mut returning: Option<DataType> = None;
33023
33024                // Keep parsing options until we see RParen
33025                while !self.check(TokenType::RParen) {
33026                    // WITH [CONDITIONAL|UNCONDITIONAL] [ARRAY] WRAPPER - match in order of specificity
33027                    if self.match_text_seq(&["WITH", "UNCONDITIONAL", "ARRAY", "WRAPPER"]) {
33028                        wrapper_option = Some("WITH UNCONDITIONAL ARRAY WRAPPER".to_string());
33029                    } else if self.match_text_seq(&["WITH", "CONDITIONAL", "ARRAY", "WRAPPER"]) {
33030                        wrapper_option = Some("WITH CONDITIONAL ARRAY WRAPPER".to_string());
33031                    } else if self.match_text_seq(&["WITH", "UNCONDITIONAL", "WRAPPER"]) {
33032                        wrapper_option = Some("WITH UNCONDITIONAL WRAPPER".to_string());
33033                    } else if self.match_text_seq(&["WITH", "CONDITIONAL", "WRAPPER"]) {
33034                        wrapper_option = Some("WITH CONDITIONAL WRAPPER".to_string());
33035                    } else if self.match_text_seq(&["WITH", "ARRAY", "WRAPPER"]) {
33036                        wrapper_option = Some("WITH ARRAY WRAPPER".to_string());
33037                    } else if self.match_text_seq(&["WITH", "WRAPPER"]) {
33038                        wrapper_option = Some("WITH WRAPPER".to_string());
33039                    // WITHOUT [CONDITIONAL] [ARRAY] WRAPPER
33040                    } else if self.match_text_seq(&["WITHOUT", "CONDITIONAL", "ARRAY", "WRAPPER"]) {
33041                        wrapper_option = Some("WITHOUT CONDITIONAL ARRAY WRAPPER".to_string());
33042                    } else if self.match_text_seq(&["WITHOUT", "CONDITIONAL", "WRAPPER"]) {
33043                        wrapper_option = Some("WITHOUT CONDITIONAL WRAPPER".to_string());
33044                    } else if self.match_text_seq(&["WITHOUT", "ARRAY", "WRAPPER"]) {
33045                        wrapper_option = Some("WITHOUT ARRAY WRAPPER".to_string());
33046                    } else if self.match_text_seq(&["WITHOUT", "WRAPPER"]) {
33047                        wrapper_option = Some("WITHOUT WRAPPER".to_string());
33048                    } else if self.match_text_seq(&["KEEP", "QUOTES"]) {
33049                        // KEEP QUOTES
33050                        quotes_option = Some("KEEP QUOTES".to_string());
33051                    } else if self.match_text_seq(&["OMIT", "QUOTES", "ON", "SCALAR", "STRING"]) {
33052                        // OMIT QUOTES ON SCALAR STRING
33053                        quotes_option = Some("OMIT QUOTES".to_string());
33054                        on_scalar_string = true;
33055                    } else if self.match_text_seq(&["OMIT", "QUOTES"]) {
33056                        // OMIT QUOTES
33057                        quotes_option = Some("OMIT QUOTES".to_string());
33058                    } else if self.match_text_seq(&["NULL", "ON", "ERROR"]) {
33059                        on_error = Some("NULL ON ERROR".to_string());
33060                    } else if self.match_text_seq(&["ERROR", "ON", "ERROR"]) {
33061                        on_error = Some("ERROR ON ERROR".to_string());
33062                    } else if self.match_text_seq(&["EMPTY", "ON", "ERROR"]) {
33063                        on_error = Some("EMPTY ON ERROR".to_string());
33064                    } else if self.match_token(TokenType::Returning) {
33065                        // RETURNING type
33066                        returning = Some(self.parse_data_type()?);
33067                    } else {
33068                        // No more options recognized, break
33069                        break;
33070                    }
33071                }
33072
33073                self.expect(TokenType::RParen)?;
33074                let func = JsonExtractFunc {
33075                    this,
33076                    path,
33077                    returning,
33078                    arrow_syntax: false,
33079                    hash_arrow_syntax: false,
33080                    wrapper_option,
33081                    quotes_option,
33082                    on_scalar_string,
33083                    on_error,
33084                };
33085                Ok(match upper_name {
33086                    "JSON_EXTRACT" => Expression::JsonExtract(Box::new(func)),
33087                    "JSON_EXTRACT_SCALAR" => Expression::JsonExtractScalar(Box::new(func)),
33088                    "JSON_QUERY" => Expression::JsonQuery(Box::new(func)),
33089                    "JSON_VALUE" => Expression::JsonValue(Box::new(func)),
33090                    _ => unreachable!("JSON function name already matched in caller"),
33091                })
33092            }
33093            // JSON_KEYS, TO_JSON, PARSE_JSON etc. support additional args including named args (BigQuery)
33094            // e.g., JSON_KEYS(expr, depth, mode => 'lax'), TO_JSON(expr, stringify_wide_numbers => FALSE)
33095            // e.g., PARSE_JSON('{}', wide_number_mode => 'exact')
33096            "JSON_ARRAY_LENGTH" | "JSON_KEYS" | "JSON_TYPE" | "TO_JSON" | "PARSE_JSON" => {
33097                let this = self.parse_expression()?;
33098                // ClickHouse: expr AS alias inside function args
33099                let this = self.maybe_clickhouse_alias(this);
33100
33101                // Check for additional arguments (comma-separated, possibly named)
33102                if self.match_token(TokenType::Comma) {
33103                    // Has additional arguments - parse as generic Function to preserve all args
33104                    let mut all_args = vec![this];
33105                    let remaining = self.parse_function_arguments()?;
33106                    all_args.extend(remaining);
33107                    self.expect(TokenType::RParen)?;
33108                    Ok(Expression::Function(Box::new(Function {
33109                        name: name.to_string(),
33110                        args: all_args,
33111                        distinct: false,
33112                        trailing_comments: Vec::new(),
33113                        use_bracket_syntax: false,
33114                        no_parens: false,
33115                        quoted: false,
33116                        span: None,
33117                        inferred_type: None,
33118                    })))
33119                } else {
33120                    // Single argument - use typed expression
33121                    self.expect(TokenType::RParen)?;
33122                    let func = UnaryFunc::new(this);
33123                    Ok(match canonical_upper_name {
33124                        "JSON_ARRAY_LENGTH" => Expression::JsonArrayLength(Box::new(func)),
33125                        "JSON_KEYS" => Expression::JsonKeys(Box::new(func)),
33126                        "JSON_TYPE" => Expression::JsonType(Box::new(func)),
33127                        "TO_JSON" => Expression::ToJson(Box::new(func)),
33128                        "PARSE_JSON" => Expression::ParseJson(Box::new(func)),
33129                        _ => unreachable!("JSON function name already matched in caller"),
33130                    })
33131                }
33132            }
33133
33134            // JSON_OBJECT with SQL standard syntax: JSON_OBJECT('key': value, ...) or JSON_OBJECT(*)
33135            "JSON_OBJECT" => {
33136                let mut pairs = Vec::new();
33137                let mut star = false;
33138                if !self.check(TokenType::RParen) {
33139                    // Check for JSON_OBJECT(*) syntax
33140                    if self.check(TokenType::Star) && self.check_next(TokenType::RParen) {
33141                        self.skip(); // consume *
33142                        star = true;
33143                    } else {
33144                        loop {
33145                            // Check for KEY keyword for KEY 'key' IS value syntax (KEY is a keyword token)
33146                            let has_key_keyword = self.match_token(TokenType::Key);
33147                            // Parse key: try string first (for 'key' syntax), then column
33148                            let key = if let Some(s) = self.parse_string()? {
33149                                s
33150                            } else {
33151                                // Use parse_column for key to avoid interpreting colon as JSON path
33152                                self.parse_column()?.ok_or_else(|| {
33153                                    self.parse_error("Expected key expression in JSON_OBJECT")
33154                                })?
33155                            };
33156
33157                            // Support colon, VALUE keyword (identifier), and IS keyword (for KEY...IS syntax)
33158                            let has_separator = self.match_token(TokenType::Colon)
33159                                || self.match_identifier("VALUE")
33160                                || (has_key_keyword && self.match_token(TokenType::Is));
33161
33162                            if has_separator {
33163                                let value = self.parse_bitwise()?.ok_or_else(|| {
33164                                    self.parse_error("Expected value expression in JSON_OBJECT")
33165                                })?;
33166                                // Check for FORMAT JSON after value
33167                                let value_with_format = if self.match_text_seq(&["FORMAT", "JSON"])
33168                                {
33169                                    Expression::JSONFormat(Box::new(JSONFormat {
33170                                        this: Some(Box::new(value)),
33171                                        options: Vec::new(),
33172                                        is_json: None,
33173                                        to_json: None,
33174                                    }))
33175                                } else {
33176                                    value
33177                                };
33178                                pairs.push((key, value_with_format));
33179                            } else {
33180                                // Just key/value pairs without separator
33181                                if self.match_token(TokenType::Comma) {
33182                                    let value = self.parse_bitwise()?.ok_or_else(|| {
33183                                        self.parse_error("Expected value expression in JSON_OBJECT")
33184                                    })?;
33185                                    pairs.push((key, value));
33186                                } else {
33187                                    return Err(self
33188                                        .parse_error("Expected value expression in JSON_OBJECT"));
33189                                }
33190                            }
33191                            if !self.match_token(TokenType::Comma) {
33192                                break;
33193                            }
33194                        }
33195                    }
33196                }
33197                // Parse optional modifiers: NULL ON NULL, ABSENT ON NULL, WITH UNIQUE KEYS
33198                let null_handling = if self.match_token(TokenType::Null) {
33199                    self.match_token(TokenType::On);
33200                    self.match_token(TokenType::Null);
33201                    Some(JsonNullHandling::NullOnNull)
33202                } else if self.match_identifier("ABSENT") {
33203                    self.match_token(TokenType::On);
33204                    self.match_token(TokenType::Null);
33205                    Some(JsonNullHandling::AbsentOnNull)
33206                } else {
33207                    None
33208                };
33209                let with_unique_keys = if self.match_token(TokenType::With) {
33210                    self.match_token(TokenType::Unique);
33211                    self.match_identifier("KEYS");
33212                    true
33213                } else {
33214                    false
33215                };
33216                // Parse optional RETURNING clause: RETURNING type [FORMAT JSON] [ENCODING encoding]
33217                let (returning_type, format_json, encoding) = if self
33218                    .match_token(TokenType::Returning)
33219                {
33220                    let return_type = self.parse_data_type()?;
33221                    // Optional FORMAT JSON
33222                    let has_format_json = if self.match_token(TokenType::Format) {
33223                        // JSON might be a keyword or identifier
33224                        let _ = self.match_token(TokenType::Json) || self.match_identifier("JSON");
33225                        true
33226                    } else {
33227                        false
33228                    };
33229                    // Optional ENCODING encoding
33230                    let enc = if self.match_identifier("ENCODING") {
33231                        Some(self.expect_identifier_or_keyword()?)
33232                    } else {
33233                        None
33234                    };
33235                    (Some(return_type), has_format_json, enc)
33236                } else {
33237                    (None, false, None)
33238                };
33239                self.expect(TokenType::RParen)?;
33240                Ok(Expression::JsonObject(Box::new(JsonObjectFunc {
33241                    pairs,
33242                    null_handling,
33243                    with_unique_keys,
33244                    returning_type,
33245                    format_json,
33246                    encoding,
33247                    star,
33248                })))
33249            }
33250
33251            // JSON_ARRAY function with Oracle-specific options
33252            // JSON_ARRAY(expr [FORMAT JSON], ... [NULL ON NULL | ABSENT ON NULL] [RETURNING type] [STRICT])
33253            "JSON_ARRAY" => {
33254                let mut expressions = Vec::new();
33255                if !self.check(TokenType::RParen) {
33256                    loop {
33257                        let expr = self.parse_bitwise()?.unwrap_or(Expression::Null(Null));
33258                        // Check for FORMAT JSON after each expression
33259                        let expr_with_format = if self.match_text_seq(&["FORMAT", "JSON"]) {
33260                            Expression::JSONFormat(Box::new(JSONFormat {
33261                                this: Some(Box::new(expr)),
33262                                options: Vec::new(),
33263                                is_json: None,
33264                                to_json: None,
33265                            }))
33266                        } else {
33267                            expr
33268                        };
33269                        expressions.push(expr_with_format);
33270                        if !self.match_token(TokenType::Comma) {
33271                            break;
33272                        }
33273                    }
33274                }
33275                // Parse NULL ON NULL or ABSENT ON NULL
33276                let null_handling = if self.match_text_seq(&["NULL", "ON", "NULL"]) {
33277                    Some(Box::new(Expression::Var(Box::new(Var {
33278                        this: "NULL ON NULL".to_string(),
33279                    }))))
33280                } else if self.match_text_seq(&["ABSENT", "ON", "NULL"]) {
33281                    Some(Box::new(Expression::Var(Box::new(Var {
33282                        this: "ABSENT ON NULL".to_string(),
33283                    }))))
33284                } else {
33285                    None
33286                };
33287                // Parse RETURNING type
33288                let return_type = if self.match_token(TokenType::Returning) {
33289                    let dt = self.parse_data_type()?;
33290                    Some(Box::new(Expression::DataType(dt)))
33291                } else {
33292                    None
33293                };
33294                // Parse STRICT
33295                let strict = if self.match_identifier("STRICT") {
33296                    Some(Box::new(Expression::Boolean(BooleanLiteral {
33297                        value: true,
33298                    })))
33299                } else {
33300                    None
33301                };
33302                self.expect(TokenType::RParen)?;
33303                Ok(Expression::JSONArray(Box::new(JSONArray {
33304                    expressions,
33305                    null_handling,
33306                    return_type,
33307                    strict,
33308                })))
33309            }
33310
33311            // JSON_ARRAYAGG function with Oracle-specific options
33312            // JSON_ARRAYAGG(expr [FORMAT JSON] [ORDER BY ...] [NULL ON NULL | ABSENT ON NULL] [RETURNING type] [STRICT])
33313            "JSON_ARRAYAGG" => {
33314                let this = self.parse_bitwise()?.unwrap_or(Expression::Null(Null));
33315                // Check for FORMAT JSON after the expression
33316                let this_with_format = if self.match_text_seq(&["FORMAT", "JSON"]) {
33317                    Expression::JSONFormat(Box::new(JSONFormat {
33318                        this: Some(Box::new(this)),
33319                        options: Vec::new(),
33320                        is_json: None,
33321                        to_json: None,
33322                    }))
33323                } else {
33324                    this
33325                };
33326                // Parse ORDER BY clause
33327                let order = if self.match_token(TokenType::Order) {
33328                    self.match_token(TokenType::By);
33329                    // Parse comma-separated ordered expressions
33330                    let mut order_exprs = Vec::new();
33331                    loop {
33332                        if let Some(ordered) = self.parse_ordered_item()? {
33333                            order_exprs.push(ordered);
33334                        } else {
33335                            break;
33336                        }
33337                        if !self.match_token(TokenType::Comma) {
33338                            break;
33339                        }
33340                    }
33341                    if !order_exprs.is_empty() {
33342                        Some(Box::new(Expression::OrderBy(Box::new(OrderBy {
33343                            expressions: order_exprs,
33344                            siblings: false,
33345                            comments: Vec::new(),
33346                        }))))
33347                    } else {
33348                        None
33349                    }
33350                } else {
33351                    None
33352                };
33353                // Parse NULL ON NULL or ABSENT ON NULL
33354                let null_handling = if self.match_text_seq(&["NULL", "ON", "NULL"]) {
33355                    Some(Box::new(Expression::Var(Box::new(Var {
33356                        this: "NULL ON NULL".to_string(),
33357                    }))))
33358                } else if self.match_text_seq(&["ABSENT", "ON", "NULL"]) {
33359                    Some(Box::new(Expression::Var(Box::new(Var {
33360                        this: "ABSENT ON NULL".to_string(),
33361                    }))))
33362                } else {
33363                    None
33364                };
33365                // Parse RETURNING type
33366                let return_type = if self.match_token(TokenType::Returning) {
33367                    let dt = self.parse_data_type()?;
33368                    Some(Box::new(Expression::DataType(dt)))
33369                } else {
33370                    None
33371                };
33372                // Parse STRICT
33373                let strict = if self.match_identifier("STRICT") {
33374                    Some(Box::new(Expression::Boolean(BooleanLiteral {
33375                        value: true,
33376                    })))
33377                } else {
33378                    None
33379                };
33380                self.expect(TokenType::RParen)?;
33381                Ok(Expression::JSONArrayAgg(Box::new(JSONArrayAgg {
33382                    this: Box::new(this_with_format),
33383                    order,
33384                    null_handling,
33385                    return_type,
33386                    strict,
33387                })))
33388            }
33389
33390            // JSON_OBJECTAGG with KEY...VALUE syntax
33391            // JSON_OBJECTAGG(KEY key VALUE value) or JSON_OBJECTAGG(key: value)
33392            "JSON_OBJECTAGG" => {
33393                // Check for KEY keyword (KEY is a keyword token, not an identifier)
33394                let _has_key_keyword = self.match_token(TokenType::Key);
33395                // Parse key: use column parsing to avoid colon being interpreted as JSON path
33396                let key = self.parse_column()?.unwrap_or(Expression::Null(Null));
33397
33398                // Support colon or VALUE keyword (VALUE is an identifier, not a keyword)
33399                let _ = self.match_token(TokenType::Colon) || self.match_identifier("VALUE");
33400
33401                let value = self.parse_bitwise()?.unwrap_or(Expression::Null(Null));
33402                // Check for FORMAT JSON after value
33403                let value_with_format = if self.match_text_seq(&["FORMAT", "JSON"]) {
33404                    Expression::JSONFormat(Box::new(JSONFormat {
33405                        this: Some(Box::new(value)),
33406                        options: Vec::new(),
33407                        is_json: None,
33408                        to_json: None,
33409                    }))
33410                } else {
33411                    value
33412                };
33413                // Parse NULL ON NULL or ABSENT ON NULL
33414                let null_handling = if self.match_text_seq(&["NULL", "ON", "NULL"]) {
33415                    Some(Box::new(Expression::Var(Box::new(Var {
33416                        this: "NULL ON NULL".to_string(),
33417                    }))))
33418                } else if self.match_text_seq(&["ABSENT", "ON", "NULL"]) {
33419                    Some(Box::new(Expression::Var(Box::new(Var {
33420                        this: "ABSENT ON NULL".to_string(),
33421                    }))))
33422                } else {
33423                    None
33424                };
33425                // Parse WITH/WITHOUT UNIQUE KEYS
33426                let unique_keys = if self.match_text_seq(&["WITH", "UNIQUE"]) {
33427                    self.match_identifier("KEYS");
33428                    Some(Box::new(Expression::Boolean(BooleanLiteral {
33429                        value: true,
33430                    })))
33431                } else if self.match_text_seq(&["WITHOUT", "UNIQUE"]) {
33432                    self.match_identifier("KEYS");
33433                    Some(Box::new(Expression::Boolean(BooleanLiteral {
33434                        value: false,
33435                    })))
33436                } else {
33437                    None
33438                };
33439                // Parse RETURNING type
33440                let return_type = if self.match_token(TokenType::Returning) {
33441                    let dt = self.parse_data_type()?;
33442                    Some(Box::new(Expression::DataType(dt)))
33443                } else {
33444                    None
33445                };
33446                self.expect(TokenType::RParen)?;
33447                Ok(Expression::JSONObjectAgg(Box::new(JSONObjectAgg {
33448                    expressions: vec![Expression::JSONKeyValue(Box::new(JSONKeyValue {
33449                        this: Box::new(key),
33450                        expression: Box::new(value_with_format),
33451                    }))],
33452                    null_handling,
33453                    unique_keys,
33454                    return_type,
33455                    encoding: None,
33456                })))
33457            }
33458
33459            // JSON_TABLE function - MySQL/Oracle table function for JSON data
33460            // JSON_TABLE(json_doc [FORMAT JSON], path COLUMNS (column_list)) [AS alias]
33461            "JSON_TABLE" => {
33462                // Parse the JSON expression
33463                let this = self.parse_bitwise()?.unwrap_or(Expression::Null(Null));
33464                // Check for FORMAT JSON after the expression
33465                let this_with_format = if self.match_text_seq(&["FORMAT", "JSON"]) {
33466                    Expression::JSONFormat(Box::new(JSONFormat {
33467                        this: Some(Box::new(this)),
33468                        options: Vec::new(),
33469                        is_json: None,
33470                        to_json: None,
33471                    }))
33472                } else {
33473                    this
33474                };
33475
33476                // Parse path (after comma)
33477                let path = if self.match_token(TokenType::Comma) {
33478                    if let Some(s) = self.parse_string()? {
33479                        Some(Box::new(s))
33480                    } else {
33481                        None
33482                    }
33483                } else {
33484                    None
33485                };
33486
33487                // Oracle uses "ERROR ON ERROR" (value then behavior) instead of "ON ERROR ERROR"
33488                // Parse error handling: ERROR ON ERROR or NULL ON ERROR
33489                let error_handling =
33490                    if self.match_identifier("ERROR") && self.match_text_seq(&["ON", "ERROR"]) {
33491                        Some(Box::new(Expression::Var(Box::new(Var {
33492                            this: "ERROR ON ERROR".to_string(),
33493                        }))))
33494                    } else if self.match_text_seq(&["NULL", "ON", "ERROR"]) {
33495                        Some(Box::new(Expression::Var(Box::new(Var {
33496                            this: "NULL ON ERROR".to_string(),
33497                        }))))
33498                    } else {
33499                        None
33500                    };
33501
33502                // Parse empty handling: ERROR ON EMPTY or NULL ON EMPTY
33503                let empty_handling =
33504                    if self.match_identifier("ERROR") && self.match_text_seq(&["ON", "EMPTY"]) {
33505                        Some(Box::new(Expression::Var(Box::new(Var {
33506                            this: "ERROR ON EMPTY".to_string(),
33507                        }))))
33508                    } else if self.match_text_seq(&["NULL", "ON", "EMPTY"]) {
33509                        Some(Box::new(Expression::Var(Box::new(Var {
33510                            this: "NULL ON EMPTY".to_string(),
33511                        }))))
33512                    } else {
33513                        None
33514                    };
33515
33516                // Parse COLUMNS clause
33517                let schema = self.parse_json_table_columns()?;
33518
33519                self.expect(TokenType::RParen)?;
33520
33521                Ok(Expression::JSONTable(Box::new(JSONTable {
33522                    this: Box::new(this_with_format),
33523                    schema: schema.map(Box::new),
33524                    path,
33525                    error_handling,
33526                    empty_handling,
33527                })))
33528            }
33529            _ => unreachable!(
33530                "phase-6 json parser called with non-json family name '{}'",
33531                canonical_upper_name
33532            ),
33533        }
33534    }
33535
33536    fn parse_typed_translate_teradata_family(
33537        &mut self,
33538        name: &str,
33539        _upper_name: &str,
33540        canonical_upper_name: &str,
33541    ) -> Result<Expression> {
33542        match canonical_upper_name {
33543            // Teradata: TRANSLATE(x USING charset [WITH ERROR])
33544            "TRANSLATE"
33545                if matches!(
33546                    self.config.dialect,
33547                    Some(crate::dialects::DialectType::Teradata)
33548                ) =>
33549            {
33550                let this = self.parse_expression()?;
33551                if self.match_token(TokenType::Using) {
33552                    let expression = self.parse_expression()?;
33553                    let with_error = if self.match_text_seq(&["WITH", "ERROR"]) {
33554                        Some(Box::new(Expression::Boolean(BooleanLiteral {
33555                            value: true,
33556                        })))
33557                    } else {
33558                        None
33559                    };
33560                    self.expect(TokenType::RParen)?;
33561                    Ok(Expression::TranslateCharacters(Box::new(
33562                        TranslateCharacters {
33563                            this: Box::new(this),
33564                            expression: Box::new(expression),
33565                            with_error,
33566                        },
33567                    )))
33568                } else {
33569                    let mut args = vec![this];
33570                    if self.match_token(TokenType::Comma) {
33571                        let mut rest = self.parse_expression_list()?;
33572                        args.append(&mut rest);
33573                    }
33574                    self.expect(TokenType::RParen)?;
33575                    Ok(Expression::Function(Box::new(Function {
33576                        name: name.to_string(),
33577                        args,
33578                        distinct: false,
33579                        trailing_comments: Vec::new(),
33580                        use_bracket_syntax: false,
33581                        no_parens: false,
33582                        quoted: false,
33583                        span: None,
33584                        inferred_type: None,
33585                    })))
33586                }
33587            }
33588
33589            _ => unreachable!(
33590                "phase-6 translate parser called with non-translate family name '{}'",
33591                canonical_upper_name
33592            ),
33593        }
33594    }
33595
33596    /// Parse a generic function call (fallback for unrecognized functions)
33597    fn parse_generic_function(&mut self, name: &str, quoted: bool) -> Result<Expression> {
33598        let is_known_agg = Self::is_aggregate_function(name);
33599
33600        let (args, distinct) = if self.check(TokenType::RParen) {
33601            (Vec::new(), false)
33602        } else if self.check(TokenType::Star) {
33603            // Check for DuckDB *COLUMNS(...) syntax first
33604            if self.check_next_identifier("COLUMNS")
33605                && self
33606                    .tokens
33607                    .get(self.current + 2)
33608                    .map(|t| t.token_type == TokenType::LParen)
33609                    .unwrap_or(false)
33610            {
33611                // Parse *COLUMNS(...) as a function argument
33612                (self.parse_function_arguments()?, false)
33613            } else {
33614                // Regular star: parse star modifiers like EXCLUDE/EXCEPT/REPLACE/RENAME
33615                // e.g., COLUMNS(* EXCLUDE (empid, dept))
33616                self.skip(); // consume *
33617                let star = self.parse_star_modifiers(None)?;
33618                let mut args = vec![Expression::Star(star)];
33619                // ClickHouse: func(*, col1, col2) — star followed by more args
33620                if self.match_token(TokenType::Comma) {
33621                    let rest = self.parse_function_arguments()?;
33622                    args.extend(rest);
33623                }
33624                (args, false)
33625            }
33626        } else if self.check(TokenType::Distinct)
33627            && !self.check_next(TokenType::Comma)
33628            && !self.check_next(TokenType::RParen)
33629        {
33630            // DISTINCT as aggregate modifier: func(DISTINCT expr)
33631            // Not when followed by comma or rparen — then DISTINCT is used as an identifier value
33632            self.skip(); // consume DISTINCT
33633            (self.parse_function_arguments()?, true)
33634        } else if is_known_agg && self.match_token(TokenType::All) {
33635            // ALL is the default quantifier, just consume it
33636            (self.parse_function_arguments()?, false)
33637        } else {
33638            (self.parse_function_arguments()?, false)
33639        };
33640
33641        // For known aggregate functions, check for IGNORE NULLS, ORDER BY, LIMIT inside parens
33642        let (ignore_nulls, order_by, agg_limit) = if is_known_agg {
33643            let ignore_nulls = if self.match_token(TokenType::Ignore)
33644                && self.match_token(TokenType::Nulls)
33645            {
33646                Some(true)
33647            } else if self.match_token(TokenType::Respect) && self.match_token(TokenType::Nulls) {
33648                Some(false)
33649            } else {
33650                None
33651            };
33652
33653            let order_by = if self.match_keywords(&[TokenType::Order, TokenType::By]) {
33654                self.parse_order_by_list()?
33655            } else {
33656                Vec::new()
33657            };
33658            let limit = if self.match_token(TokenType::Limit) {
33659                Some(Box::new(self.parse_expression()?))
33660            } else {
33661                None
33662            };
33663            (ignore_nulls, order_by, limit)
33664        } else {
33665            (None, Vec::new(), None)
33666        };
33667
33668        // ClickHouse: SETTINGS key=value, ... before closing paren in function calls
33669        if matches!(
33670            self.config.dialect,
33671            Some(crate::dialects::DialectType::ClickHouse)
33672        ) && self.check(TokenType::Settings)
33673            && self.current + 2 < self.tokens.len()
33674            && (self.tokens[self.current + 1].token_type == TokenType::Var
33675                || self.tokens[self.current + 1].token_type == TokenType::Identifier)
33676            && self.tokens[self.current + 2].token_type == TokenType::Eq
33677        {
33678            self.skip(); // consume SETTINGS
33679            loop {
33680                let _key = if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
33681                    self.advance().text
33682                } else {
33683                    break;
33684                };
33685                if self.match_token(TokenType::Eq) {
33686                    let _value = self.parse_primary()?;
33687                }
33688                if !self.match_token(TokenType::Comma) {
33689                    break;
33690                }
33691            }
33692        }
33693
33694        self.expect(TokenType::RParen)?;
33695        let trailing_comments = self.previous_trailing_comments().to_vec();
33696
33697        // Check for WITHIN GROUP (ORDER BY ...)
33698        if self.match_identifier("WITHIN") {
33699            if self.match_identifier("GROUP") {
33700                self.expect(TokenType::LParen)?;
33701                self.expect(TokenType::Order)?;
33702                self.expect(TokenType::By)?;
33703                let within_order = self.parse_order_by_list()?;
33704                self.expect(TokenType::RParen)?;
33705
33706                let func_expr = Expression::AggregateFunction(Box::new(AggregateFunction {
33707                    name: name.to_string(),
33708                    args,
33709                    distinct,
33710                    filter: None,
33711                    order_by: Vec::new(),
33712                    limit: None,
33713                    ignore_nulls: None,
33714                    inferred_type: None,
33715                }));
33716
33717                let within = Expression::WithinGroup(Box::new(WithinGroup {
33718                    this: func_expr,
33719                    order_by: within_order,
33720                }));
33721
33722                // Check for FILTER after WITHIN GROUP
33723                let filter = self.parse_filter_clause()?;
33724                if let Some(filter_expr) = filter {
33725                    return Ok(Expression::AggregateFunction(Box::new(AggregateFunction {
33726                        name: format!("__WITHIN_GROUP_{}", name),
33727                        args: vec![within, filter_expr],
33728                        distinct: false,
33729                        filter: None,
33730                        order_by: Vec::new(),
33731                        limit: None,
33732                        ignore_nulls: None,
33733                        inferred_type: None,
33734                    })));
33735                }
33736
33737                return Ok(within);
33738            }
33739        }
33740
33741        let filter = self.parse_filter_clause()?;
33742
33743        // Check for postfix IGNORE NULLS / RESPECT NULLS after RParen
33744        let ignore_nulls = if ignore_nulls.is_some() {
33745            ignore_nulls
33746        } else if self.match_keywords(&[TokenType::Ignore, TokenType::Nulls]) {
33747            Some(true)
33748        } else if self.match_keywords(&[TokenType::Respect, TokenType::Nulls]) {
33749            Some(false)
33750        } else {
33751            None
33752        };
33753
33754        if filter.is_some() || is_known_agg || ignore_nulls.is_some() {
33755            Ok(Expression::AggregateFunction(Box::new(AggregateFunction {
33756                name: name.to_string(),
33757                args,
33758                distinct,
33759                filter,
33760                order_by,
33761                limit: agg_limit,
33762                ignore_nulls,
33763                inferred_type: None,
33764            })))
33765        } else {
33766            let mut func = Function::new(name.to_string(), args);
33767            func.distinct = distinct;
33768            func.trailing_comments = trailing_comments;
33769            func.quoted = quoted;
33770            Ok(Expression::Function(Box::new(func)))
33771        }
33772    }
33773
33774    /// Check for an AS alias after an expression in ClickHouse function arg context.
33775    fn maybe_clickhouse_alias(&mut self, expr: Expression) -> Expression {
33776        if matches!(
33777            self.config.dialect,
33778            Some(crate::dialects::DialectType::ClickHouse)
33779        ) && self.check(TokenType::As)
33780            && !self.check_next(TokenType::RParen)
33781            && !self.check_next(TokenType::Comma)
33782        {
33783            let next_idx = self.current + 1;
33784            let is_alias = next_idx < self.tokens.len()
33785                && matches!(
33786                    self.tokens[next_idx].token_type,
33787                    TokenType::Identifier | TokenType::Var | TokenType::QuotedIdentifier
33788                );
33789            if is_alias {
33790                self.skip(); // consume AS
33791                let alias_token = self.advance();
33792                let alias_name = Identifier {
33793                    name: alias_token.text.clone(),
33794                    quoted: alias_token.token_type == TokenType::QuotedIdentifier,
33795                    trailing_comments: Vec::new(),
33796                    span: None,
33797                };
33798                return Expression::Alias(Box::new(crate::expressions::Alias {
33799                    this: expr,
33800                    alias: alias_name,
33801                    column_aliases: Vec::new(),
33802                    pre_alias_comments: Vec::new(),
33803                    trailing_comments: Vec::new(),
33804                    inferred_type: None,
33805                }));
33806            }
33807        }
33808        expr
33809    }
33810
33811    /// Parse an expression, then check for AS alias in ClickHouse function arg context.
33812    /// ClickHouse allows: func(expr AS alias, ...) where AS creates a named alias inside function args.
33813    fn parse_expression_with_clickhouse_alias(&mut self) -> Result<Expression> {
33814        let expr = self.parse_expression()?;
33815        Ok(self.maybe_clickhouse_alias(expr))
33816    }
33817
33818    /// Parse function arguments, handling named arguments (name => value, name := value)
33819    /// and TABLE/MODEL prefixed arguments (BigQuery)
33820    fn parse_function_arguments(&mut self) -> Result<Vec<Expression>> {
33821        let mut args = Vec::new();
33822
33823        loop {
33824            // ClickHouse: SETTINGS key=value, ... terminates function args
33825            // Only break if SETTINGS is followed by identifier = value pattern
33826            if matches!(
33827                self.config.dialect,
33828                Some(crate::dialects::DialectType::ClickHouse)
33829            ) && self.check(TokenType::Settings)
33830                && self.current + 2 < self.tokens.len()
33831                && (self.tokens[self.current + 1].token_type == TokenType::Var
33832                    || self.tokens[self.current + 1].token_type == TokenType::Identifier)
33833                && self.tokens[self.current + 2].token_type == TokenType::Eq
33834            {
33835                break; // will be consumed by SETTINGS handler after loop
33836            }
33837
33838            // ClickHouse: bare SELECT/WITH as function argument (e.g., view(SELECT 1), remote(..., view(SELECT ...)))
33839            if matches!(
33840                self.config.dialect,
33841                Some(crate::dialects::DialectType::ClickHouse)
33842            ) && (self.check(TokenType::Select) || self.check(TokenType::With))
33843            {
33844                let query = self.parse_statement()?;
33845                args.push(query);
33846                if !self.match_token(TokenType::Comma) {
33847                    break;
33848                }
33849                continue;
33850            }
33851
33852            // Check for TABLE ref or MODEL ref as function argument (BigQuery)
33853            // e.g., GAP_FILL(TABLE device_data, ...) or ML.PREDICT(MODEL mydataset.mymodel, ...)
33854            let is_table_or_model_arg = if !self.is_at_end() {
33855                self.check(TokenType::Table) || self.peek().text.eq_ignore_ascii_case("MODEL")
33856            } else {
33857                false
33858            };
33859            let arg = if is_table_or_model_arg {
33860                let prefix = self.peek().text.to_ascii_uppercase();
33861                let saved_pos = self.current;
33862                self.skip(); // consume TABLE or MODEL
33863
33864                // Only treat as TABLE/MODEL argument if followed by an identifier (table name),
33865                // not by => (which would be a named arg like "table => value")
33866                if !self.is_at_end()
33867                    && !self.check(TokenType::FArrow)
33868                    && !self.check(TokenType::ColonEq)
33869                {
33870                    // Parse the table/model reference (supports dotted names like dataset.table)
33871                    if let Some(table_expr) = self.parse_table_parts()? {
33872                        Expression::TableArgument(Box::new(TableArgument {
33873                            prefix,
33874                            this: table_expr,
33875                        }))
33876                    } else {
33877                        // Failed to parse table parts, backtrack and treat as regular expression
33878                        self.current = saved_pos;
33879                        self.parse_expression()?
33880                    }
33881                } else {
33882                    // TABLE/MODEL followed by => or :=, backtrack and handle as named arg
33883                    self.current = saved_pos;
33884                    if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
33885                        let ident_token = self.advance();
33886                        let ident_name = ident_token.text.clone();
33887                        if self.match_token(TokenType::FArrow) {
33888                            let value = self.parse_expression()?;
33889                            Expression::NamedArgument(Box::new(NamedArgument {
33890                                name: Identifier::new(ident_name),
33891                                value,
33892                                separator: NamedArgSeparator::DArrow,
33893                            }))
33894                        } else if self.match_token(TokenType::ColonEq) {
33895                            let value = self.parse_expression()?;
33896                            Expression::NamedArgument(Box::new(NamedArgument {
33897                                name: Identifier::new(ident_name),
33898                                value,
33899                                separator: NamedArgSeparator::ColonEq,
33900                            }))
33901                        } else {
33902                            self.current = saved_pos;
33903                            self.parse_expression()?
33904                        }
33905                    } else {
33906                        self.parse_expression()?
33907                    }
33908                }
33909            } else if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
33910                // Try to parse:
33911                // 1. Named argument: identifier => value or identifier := value
33912                // 2. Snowflake lambda with type: identifier type -> body (e.g., a int -> a + 1)
33913                // Save position to backtrack if not a named argument
33914                let saved_pos = self.current;
33915
33916                // Try to get identifier
33917                let ident_token = self.advance();
33918                let ident_name = ident_token.text.clone();
33919
33920                // PostgreSQL/Redshift VARIADIC keyword: backtrack and let parse_expression handle it
33921                // VARIADIC ARRAY[...] must not be misinterpreted as a lambda with type annotation
33922                if ident_name.eq_ignore_ascii_case("VARIADIC")
33923                    && matches!(
33924                        self.config.dialect,
33925                        Some(crate::dialects::DialectType::PostgreSQL)
33926                            | Some(crate::dialects::DialectType::Redshift)
33927                    )
33928                {
33929                    self.current = saved_pos;
33930                    self.parse_expression()?
33931                }
33932                // Check for Snowflake lambda with type annotation: a int -> body
33933                // Look ahead to see if we have a type token followed by ->
33934                else if !self.is_at_end()
33935                    && self.is_type_keyword()
33936                    && !self.check(TokenType::FArrow)
33937                    && !self.check(TokenType::ColonEq)
33938                {
33939                    // Parse type annotation
33940                    let type_annotation = self.parse_data_type()?;
33941
33942                    // Check for arrow
33943                    if self.match_token(TokenType::Arrow) {
33944                        // This is a Snowflake lambda: param type -> body
33945                        let body = self.parse_expression()?;
33946                        Expression::Lambda(Box::new(LambdaExpr {
33947                            parameters: vec![Identifier::new(ident_name)],
33948                            body,
33949                            colon: false,
33950                            parameter_types: vec![Some(type_annotation)],
33951                        }))
33952                    } else {
33953                        // Not a lambda, backtrack and parse as regular expression
33954                        self.current = saved_pos;
33955                        self.parse_expression()?
33956                    }
33957                }
33958                // ClickHouse: simple lambda without type annotation: ident -> body
33959                else if self.match_token(TokenType::Arrow) {
33960                    let body = self.parse_expression()?;
33961                    Expression::Lambda(Box::new(LambdaExpr {
33962                        parameters: vec![Identifier::new(ident_name)],
33963                        body,
33964                        colon: false,
33965                        parameter_types: Vec::new(),
33966                    }))
33967                }
33968                // Check for named argument separator (=> is FArrow)
33969                else if self.match_token(TokenType::FArrow) {
33970                    // name => value
33971                    let value = self.parse_expression()?;
33972                    Expression::NamedArgument(Box::new(NamedArgument {
33973                        name: Identifier::new(ident_name),
33974                        value,
33975                        separator: NamedArgSeparator::DArrow,
33976                    }))
33977                } else if self.match_token(TokenType::ColonEq) {
33978                    // name := value
33979                    let value = self.parse_expression()?;
33980                    Expression::NamedArgument(Box::new(NamedArgument {
33981                        name: Identifier::new(ident_name),
33982                        value,
33983                        separator: NamedArgSeparator::ColonEq,
33984                    }))
33985                } else {
33986                    // Not a named argument, backtrack and parse as regular expression
33987                    self.current = saved_pos;
33988                    self.parse_expression()?
33989                }
33990            } else {
33991                // Regular expression
33992                self.parse_expression()?
33993            };
33994
33995            // Handle AS alias inside function arguments (e.g. ClickHouse: arrayJoin([1,2,3] AS src))
33996            let arg = if matches!(
33997                self.config.dialect,
33998                Some(crate::dialects::DialectType::ClickHouse)
33999            ) && self.check(TokenType::As)
34000                && !self.check_next(TokenType::RParen)
34001                && !self.check_next(TokenType::Comma)
34002            {
34003                // Look ahead: AS followed by identifier/keyword, then ) or , means it's an alias
34004                let next_idx = self.current + 1;
34005                let after_alias_idx = self.current + 2;
34006                let is_alias_token = next_idx < self.tokens.len()
34007                    && (matches!(
34008                        self.tokens[next_idx].token_type,
34009                        TokenType::Identifier | TokenType::Var | TokenType::QuotedIdentifier
34010                    ) || self.tokens[next_idx].token_type.is_keyword());
34011                // Ensure the token AFTER the alias is ) or , (function arg boundary)
34012                let is_alias = is_alias_token
34013                    && after_alias_idx < self.tokens.len()
34014                    && matches!(
34015                        self.tokens[after_alias_idx].token_type,
34016                        TokenType::RParen | TokenType::Comma
34017                    );
34018                if is_alias {
34019                    self.skip(); // consume AS
34020                    let alias_token = self.advance();
34021                    let alias_name = if alias_token.token_type == TokenType::QuotedIdentifier {
34022                        let mut ident = Identifier::new(alias_token.text.clone());
34023                        ident.quoted = true;
34024                        ident
34025                    } else {
34026                        Identifier::new(alias_token.text.clone())
34027                    };
34028                    Expression::Alias(Box::new(crate::expressions::Alias {
34029                        this: arg,
34030                        alias: alias_name,
34031                        column_aliases: Vec::new(),
34032                        pre_alias_comments: Vec::new(),
34033                        trailing_comments: Vec::new(),
34034                        inferred_type: None,
34035                    }))
34036                } else {
34037                    arg
34038                }
34039            } else {
34040                arg
34041            };
34042
34043            // ClickHouse: implicit alias without AS keyword: func(expr identifier, ...)
34044            let arg = self.try_clickhouse_implicit_alias(arg);
34045
34046            // Handle trailing comments
34047            let trailing_comments = self.previous_trailing_comments().to_vec();
34048            let arg = if trailing_comments.is_empty() {
34049                arg
34050            } else {
34051                match &arg {
34052                    Expression::Literal(_) | Expression::Boolean(_) | Expression::Null(_) => {
34053                        Expression::Annotated(Box::new(Annotated {
34054                            this: arg,
34055                            trailing_comments,
34056                        }))
34057                    }
34058                    _ => arg,
34059                }
34060            };
34061
34062            args.push(arg);
34063
34064            if !self.match_token(TokenType::Comma) {
34065                break;
34066            }
34067            // Skip consecutive commas (Snowflake allows skipping optional named args)
34068            // e.g., ROUND(SCALE => 1, EXPR => 2.25, , ROUNDING_MODE => 'HALF_TO_EVEN')
34069            while self.check(TokenType::Comma) {
34070                self.skip();
34071            }
34072        }
34073
34074        // ClickHouse: SETTINGS key=value, ... at end of function args before RParen
34075        if matches!(
34076            self.config.dialect,
34077            Some(crate::dialects::DialectType::ClickHouse)
34078        ) && self.check(TokenType::Settings)
34079            && self.current + 2 < self.tokens.len()
34080            && (self.tokens[self.current + 1].token_type == TokenType::Var
34081                || self.tokens[self.current + 1].token_type == TokenType::Identifier)
34082            && self.tokens[self.current + 2].token_type == TokenType::Eq
34083        {
34084            self.skip(); // consume SETTINGS
34085            loop {
34086                let _key = if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
34087                    self.advance().text
34088                } else {
34089                    break;
34090                };
34091                if self.match_token(TokenType::Eq) {
34092                    let _value = self.parse_primary()?;
34093                }
34094                if !self.match_token(TokenType::Comma) {
34095                    break;
34096                }
34097            }
34098        }
34099
34100        Ok(args)
34101    }
34102
34103    /// Parse optional FILTER clause
34104    fn parse_filter_clause(&mut self) -> Result<Option<Expression>> {
34105        if self.match_token(TokenType::Filter) {
34106            self.expect(TokenType::LParen)?;
34107            // WHERE is optional (DuckDB allows FILTER(condition) without WHERE)
34108            self.match_token(TokenType::Where);
34109            let filter_expr = self.parse_expression()?;
34110            self.expect(TokenType::RParen)?;
34111            Ok(Some(filter_expr))
34112        } else {
34113            Ok(None)
34114        }
34115    }
34116
34117    /// Parse STRUCT arguments with optional AS aliases: STRUCT(x, y AS name, ...)
34118    fn parse_struct_args(&mut self) -> Result<Vec<Expression>> {
34119        let mut args = Vec::new();
34120
34121        loop {
34122            let expr = self.parse_expression()?;
34123
34124            // Check for AS alias
34125            if self.match_token(TokenType::As) {
34126                let alias = self.expect_identifier_or_keyword()?;
34127                args.push(Expression::Alias(Box::new(Alias {
34128                    this: expr,
34129                    alias: Identifier::new(alias),
34130                    column_aliases: Vec::new(),
34131                    pre_alias_comments: Vec::new(),
34132                    trailing_comments: Vec::new(),
34133                    inferred_type: None,
34134                })));
34135            } else {
34136                args.push(expr);
34137            }
34138
34139            if !self.match_token(TokenType::Comma) {
34140                break;
34141            }
34142        }
34143
34144        Ok(args)
34145    }
34146
34147    /// Maybe parse OVER clause for window functions or WITHIN GROUP for ordered-set aggregates
34148    fn maybe_parse_over(&mut self, expr: Expression) -> Result<Expression> {
34149        let expr = self.maybe_parse_subscript(expr)?;
34150
34151        // For Oracle: Check for interval span after expression (e.g., (expr) DAY(9) TO SECOND(3))
34152        // https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/Interval-Expressions.html
34153        let expr = if matches!(
34154            self.config.dialect,
34155            Some(crate::dialects::DialectType::Oracle)
34156        ) {
34157            self.try_parse_oracle_interval_span(expr)?
34158        } else {
34159            expr
34160        };
34161
34162        // Check for WITHIN GROUP (for ordered-set aggregate functions like LISTAGG, PERCENTILE_CONT)
34163        let expr = if self.check(TokenType::Within) && self.check_next(TokenType::Group) {
34164            self.skip(); // consume WITHIN
34165            self.skip(); // consume GROUP
34166            self.expect(TokenType::LParen)?;
34167            self.expect(TokenType::Order)?;
34168            self.expect(TokenType::By)?;
34169            let order_by = self.parse_order_by_list()?;
34170            self.expect(TokenType::RParen)?;
34171            Expression::WithinGroup(Box::new(WithinGroup {
34172                this: expr,
34173                order_by,
34174            }))
34175        } else {
34176            expr
34177        };
34178
34179        // Check for FILTER clause (can follow WITHIN GROUP or standalone aggregate)
34180        // SQL:2003 syntax: aggregate_function(...) FILTER (WHERE condition)
34181        let expr = if self.match_token(TokenType::Filter) {
34182            self.expect(TokenType::LParen)?;
34183            // WHERE is required in standard SQL FILTER clause
34184            self.expect(TokenType::Where)?;
34185            let filter_expr = self.parse_expression()?;
34186            self.expect(TokenType::RParen)?;
34187            Expression::Filter(Box::new(Filter {
34188                this: Box::new(expr),
34189                expression: Box::new(filter_expr),
34190            }))
34191        } else {
34192            expr
34193        };
34194
34195        // ClickHouse: IGNORE NULLS / RESPECT NULLS modifier after function call (before OVER)
34196        // This handles cases like: func(args) IGNORE NULLS OVER w
34197        // and parametric aggregates: func(params)(args) IGNORE NULLS
34198        let expr = if matches!(
34199            self.config.dialect,
34200            Some(crate::dialects::DialectType::ClickHouse)
34201        ) && (self.match_keywords(&[TokenType::Ignore, TokenType::Nulls])
34202            || self.match_keywords(&[TokenType::Respect, TokenType::Nulls]))
34203        {
34204            // Consume the modifier — we don't need to store it for transpilation
34205            expr
34206        } else {
34207            expr
34208        };
34209
34210        // Check for KEEP clause (Oracle: aggregate KEEP (DENSE_RANK FIRST|LAST ORDER BY ...))
34211        // Only if KEEP is followed by LPAREN - otherwise KEEP is used as an alias
34212        let keep = if self.check(TokenType::Keep) && self.check_next(TokenType::LParen) {
34213            self.skip(); // consume KEEP
34214            Some(self.parse_keep_clause()?)
34215        } else {
34216            None
34217        };
34218
34219        // Check for OVER clause (can follow KEEP, FILTER, WITHIN GROUP, or standalone aggregate)
34220        if self.match_token(TokenType::Over) {
34221            let over = self.parse_over_clause()?;
34222            Ok(Expression::WindowFunction(Box::new(WindowFunction {
34223                this: expr,
34224                over,
34225                keep,
34226                inferred_type: None,
34227            })))
34228        } else if keep.is_some() {
34229            // KEEP without OVER - still a window-like construct
34230            // Create a WindowFunction with empty Over
34231            Ok(Expression::WindowFunction(Box::new(WindowFunction {
34232                this: expr,
34233                over: Over {
34234                    window_name: None,
34235                    partition_by: Vec::new(),
34236                    order_by: Vec::new(),
34237                    frame: None,
34238                    alias: None,
34239                },
34240                keep,
34241                inferred_type: None,
34242            })))
34243        } else {
34244            Ok(expr)
34245        }
34246    }
34247
34248    /// ClickHouse: parse parameterized aggregate functions like func(params)(args)
34249    fn maybe_parse_clickhouse_parameterized_agg(&mut self, expr: Expression) -> Result<Expression> {
34250        if !matches!(
34251            self.config.dialect,
34252            Some(crate::dialects::DialectType::ClickHouse)
34253        ) {
34254            return Ok(expr);
34255        }
34256        if !self.check(TokenType::LParen) {
34257            return Ok(expr);
34258        }
34259
34260        let (name, quoted, params) = match expr {
34261            Expression::Function(func) => (func.name, func.quoted, func.args),
34262            Expression::AggregateFunction(agg) => {
34263                if agg.distinct
34264                    || agg.filter.is_some()
34265                    || !agg.order_by.is_empty()
34266                    || agg.limit.is_some()
34267                    || agg.ignore_nulls.is_some()
34268                {
34269                    return Ok(Expression::AggregateFunction(agg));
34270                }
34271                (agg.name, false, agg.args)
34272            }
34273            _ => return Ok(expr),
34274        };
34275
34276        self.skip(); // consume (
34277                        // Handle DISTINCT in second arg list: func(params)(DISTINCT args)
34278        let distinct = self.match_token(TokenType::Distinct);
34279        let expressions = if self.check(TokenType::RParen) {
34280            Vec::new()
34281        } else {
34282            self.parse_function_arguments()?
34283        };
34284        self.expect(TokenType::RParen)?;
34285
34286        let ident = Identifier {
34287            name,
34288            quoted,
34289            trailing_comments: Vec::new(),
34290            span: None,
34291        };
34292
34293        // If DISTINCT was used, wrap the result to indicate it
34294        // For now, we just include it in the CombinedParameterizedAgg
34295        let _ = distinct; // DISTINCT is consumed but not separately tracked in this AST node
34296        Ok(Expression::CombinedParameterizedAgg(Box::new(
34297            CombinedParameterizedAgg {
34298                this: Box::new(Expression::Identifier(ident)),
34299                params,
34300                expressions,
34301            },
34302        )))
34303    }
34304
34305    /// Parse Oracle KEEP clause: KEEP (DENSE_RANK FIRST|LAST ORDER BY ...)
34306    fn parse_keep_clause(&mut self) -> Result<Keep> {
34307        self.expect(TokenType::LParen)?;
34308
34309        // Expect DENSE_RANK
34310        if !self.match_identifier("DENSE_RANK") {
34311            return Err(self.parse_error("Expected DENSE_RANK in KEEP clause"));
34312        }
34313
34314        // Expect FIRST or LAST
34315        let first = if self.match_token(TokenType::First) {
34316            true
34317        } else if self.match_token(TokenType::Last) {
34318            false
34319        } else {
34320            return Err(self.parse_error("Expected FIRST or LAST in KEEP clause"));
34321        };
34322
34323        // Expect ORDER BY
34324        self.expect(TokenType::Order)?;
34325        self.expect(TokenType::By)?;
34326
34327        let order_by = self.parse_order_by_list()?;
34328
34329        self.expect(TokenType::RParen)?;
34330
34331        Ok(Keep { first, order_by })
34332    }
34333
34334    /// Parse a JSON path operand - just the immediate literal/identifier without any subscript processing
34335    /// This is used for JSON arrow operators (->, ->>) to get proper left-to-right associativity
34336    fn parse_json_path_operand(&mut self) -> Result<Expression> {
34337        // Negative number literal (e.g., -1)
34338        if self.check(TokenType::Dash) {
34339            let dash_pos = self.current;
34340            self.skip(); // consume the dash
34341            if self.check(TokenType::Number) {
34342                let token = self.advance();
34343                return Ok(Expression::Neg(Box::new(UnaryOp {
34344                    this: Expression::Literal(Literal::Number(token.text)),
34345                    inferred_type: None,
34346                })));
34347            }
34348            // Not a negative number, backtrack
34349            self.current = dash_pos;
34350        }
34351
34352        // Number literal
34353        if self.check(TokenType::Number) {
34354            let token = self.advance();
34355            // Check for numeric literal suffix encoded as "number::TYPE" by tokenizer
34356            if let Some(sep_pos) = token.text.find("::") {
34357                let num_part = &token.text[..sep_pos];
34358                let type_name = &token.text[sep_pos + 2..];
34359                let num_expr = Expression::Literal(Literal::Number(num_part.to_string()));
34360                let data_type = match type_name {
34361                    "BIGINT" => crate::expressions::DataType::BigInt { length: None },
34362                    "SMALLINT" => crate::expressions::DataType::SmallInt { length: None },
34363                    "TINYINT" => crate::expressions::DataType::TinyInt { length: None },
34364                    "DOUBLE" => crate::expressions::DataType::Double {
34365                        precision: None,
34366                        scale: None,
34367                    },
34368                    "FLOAT" => crate::expressions::DataType::Float {
34369                        precision: None,
34370                        scale: None,
34371                        real_spelling: false,
34372                    },
34373                    "DECIMAL" => crate::expressions::DataType::Decimal {
34374                        precision: None,
34375                        scale: None,
34376                    },
34377                    _ => crate::expressions::DataType::Custom {
34378                        name: type_name.to_string(),
34379                    },
34380                };
34381                return Ok(Expression::TryCast(Box::new(crate::expressions::Cast {
34382                    this: num_expr,
34383                    to: data_type,
34384                    trailing_comments: Vec::new(),
34385                    double_colon_syntax: false,
34386                    format: None,
34387                    default: None,
34388                    inferred_type: None,
34389                })));
34390            }
34391            return Ok(Expression::Literal(Literal::Number(token.text)));
34392        }
34393
34394        // String literal
34395        if self.check(TokenType::String) {
34396            let token = self.advance();
34397            return Ok(Expression::Literal(Literal::String(token.text)));
34398        }
34399
34400        // Parenthesized expression (for complex paths)
34401        if self.match_token(TokenType::LParen) {
34402            let expr = self.parse_expression()?;
34403            self.expect(TokenType::RParen)?;
34404            return Ok(Expression::Paren(Box::new(Paren {
34405                this: expr,
34406                trailing_comments: Vec::new(),
34407            })));
34408        }
34409
34410        // Array literal: ['$.family', '$.species']
34411        // Used in DuckDB for multi-path JSON extraction
34412        if self.match_token(TokenType::LBracket) {
34413            // Empty array: []
34414            if self.match_token(TokenType::RBracket) {
34415                return Ok(Expression::ArrayFunc(Box::new(ArrayConstructor {
34416                    expressions: Vec::new(),
34417                    bracket_notation: true,
34418                    use_list_keyword: false,
34419                })));
34420            }
34421
34422            // Parse array elements
34423            let mut expressions = vec![self.parse_expression()?];
34424            while self.match_token(TokenType::Comma) {
34425                if self.check(TokenType::RBracket) {
34426                    break;
34427                }
34428                expressions.push(self.parse_expression()?);
34429            }
34430            self.expect(TokenType::RBracket)?;
34431
34432            return Ok(Expression::ArrayFunc(Box::new(ArrayConstructor {
34433                expressions,
34434                bracket_notation: true,
34435                use_list_keyword: false,
34436            })));
34437        }
34438
34439        // Identifier (possibly qualified like table.column)
34440        if self.is_identifier_token() {
34441            let first_ident = self.expect_identifier_with_quoted()?;
34442
34443            // Check for qualified name: identifier.identifier
34444            if self.match_token(TokenType::Dot) {
34445                if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
34446                    let second_ident = if self.is_identifier_token() {
34447                        self.expect_identifier_with_quoted()?
34448                    } else {
34449                        let token = self.advance();
34450                        Identifier::new(token.text)
34451                    };
34452                    return Ok(Expression::boxed_column(Column {
34453                        name: second_ident,
34454                        table: Some(first_ident),
34455                        join_mark: false,
34456                        trailing_comments: Vec::new(),
34457                        span: None,
34458                        inferred_type: None,
34459                    }));
34460                }
34461            }
34462
34463            return Ok(Expression::boxed_column(Column {
34464                name: first_ident,
34465                table: None,
34466                join_mark: false,
34467                trailing_comments: Vec::new(),
34468                span: None,
34469                inferred_type: None,
34470            }));
34471        }
34472
34473        // Keywords as identifiers (possibly qualified)
34474        if self.is_safe_keyword_as_identifier() {
34475            let token = self.advance();
34476            let first_ident = Identifier::new(token.text);
34477
34478            // Check for qualified name: identifier.identifier
34479            if self.match_token(TokenType::Dot) {
34480                if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
34481                    let second_ident = if self.is_identifier_token() {
34482                        self.expect_identifier_with_quoted()?
34483                    } else {
34484                        let token = self.advance();
34485                        Identifier::new(token.text)
34486                    };
34487                    return Ok(Expression::boxed_column(Column {
34488                        name: second_ident,
34489                        table: Some(first_ident),
34490                        join_mark: false,
34491                        trailing_comments: Vec::new(),
34492                        span: None,
34493                        inferred_type: None,
34494                    }));
34495                }
34496            }
34497
34498            return Ok(Expression::boxed_column(Column {
34499                name: first_ident,
34500                table: None,
34501                join_mark: false,
34502                trailing_comments: Vec::new(),
34503                span: None,
34504                inferred_type: None,
34505            }));
34506        }
34507
34508        Err(self.parse_error(format!(
34509            "Unexpected token in JSON path: {:?}",
34510            self.peek().token_type
34511        )))
34512    }
34513
34514    /// Maybe parse subscript access (array[index], struct.field)
34515    fn maybe_parse_subscript(&mut self, mut expr: Expression) -> Result<Expression> {
34516        loop {
34517            if self.match_token(TokenType::LBracket) {
34518                // Check if expr is an array/list constructor keyword (ARRAY[...] or LIST[...])
34519                let array_constructor_type = match &expr {
34520                    Expression::Column(col) if col.table.is_none() => {
34521                        let upper = col.name.name.to_ascii_uppercase();
34522                        if upper == "ARRAY" || upper == "LIST" {
34523                            Some(upper)
34524                        } else {
34525                            None
34526                        }
34527                    }
34528                    Expression::Identifier(id) => {
34529                        let upper = id.name.to_ascii_uppercase();
34530                        if upper == "ARRAY" || upper == "LIST" {
34531                            Some(upper)
34532                        } else {
34533                            None
34534                        }
34535                    }
34536                    _ => None,
34537                };
34538
34539                if let Some(constructor_type) = array_constructor_type {
34540                    // Parse ARRAY[expr, expr, ...] or LIST[expr, expr, ...]
34541                    // bracket_notation=false means we have the ARRAY/LIST keyword prefix
34542                    let use_list_keyword = constructor_type == "LIST";
34543                    if self.check(TokenType::RBracket) {
34544                        // Empty array: ARRAY[]
34545                        self.skip();
34546                        expr = Expression::ArrayFunc(Box::new(ArrayConstructor {
34547                            expressions: Vec::new(),
34548                            bracket_notation: false, // Has ARRAY/LIST keyword
34549                            use_list_keyword,
34550                        }));
34551                    } else {
34552                        let expressions = self.parse_expression_list()?;
34553                        self.expect(TokenType::RBracket)?;
34554                        expr = Expression::ArrayFunc(Box::new(ArrayConstructor {
34555                            expressions,
34556                            bracket_notation: false, // Has ARRAY/LIST keyword
34557                            use_list_keyword,
34558                        }));
34559                    }
34560                    continue;
34561                }
34562
34563                // Special case: MAP[...] constructor syntax
34564                // Check if expr is a MAP identifier
34565                // ClickHouse: map[key] is always subscript access, not a MAP constructor
34566                let is_map_constructor = !matches!(
34567                    self.config.dialect,
34568                    Some(crate::dialects::DialectType::ClickHouse)
34569                ) && match &expr {
34570                    Expression::Column(col) => {
34571                        col.name.name.eq_ignore_ascii_case("MAP") && col.table.is_none()
34572                    }
34573                    Expression::Identifier(id) => id.name.eq_ignore_ascii_case("MAP"),
34574                    _ => false,
34575                };
34576
34577                if is_map_constructor {
34578                    let is_materialize = matches!(
34579                        self.config.dialect,
34580                        Some(crate::dialects::DialectType::Materialize)
34581                    );
34582
34583                    // Materialize: MAP[] empty map or MAP['a' => 1, ...] with fat arrow
34584                    if is_materialize {
34585                        if self.check(TokenType::RBracket) {
34586                            // Empty map: MAP[]
34587                            self.skip();
34588                            expr = Expression::ToMap(Box::new(ToMap {
34589                                this: Box::new(Expression::Struct(Box::new(Struct {
34590                                    fields: Vec::new(),
34591                                }))),
34592                            }));
34593                            continue;
34594                        }
34595
34596                        // Parse MAP['a' => 1, 'b' => 2, ...] with fat arrow entries
34597                        // Store entries as PropertyEQ expressions (key => value)
34598                        let mut entries = Vec::new();
34599                        loop {
34600                            let key = self.parse_expression()?;
34601                            self.expect(TokenType::FArrow)?;
34602                            let value = self.parse_expression()?;
34603                            // Store as PropertyEQ which will be output as key => value
34604                            entries.push((
34605                                None,
34606                                Expression::PropertyEQ(Box::new(BinaryOp::new(key, value))),
34607                            ));
34608
34609                            if !self.match_token(TokenType::Comma) {
34610                                break;
34611                            }
34612                        }
34613                        self.expect(TokenType::RBracket)?;
34614
34615                        expr = Expression::ToMap(Box::new(ToMap {
34616                            this: Box::new(Expression::Struct(Box::new(Struct {
34617                                fields: entries,
34618                            }))),
34619                        }));
34620                        continue;
34621                    }
34622
34623                    // DuckDB/BigQuery: MAP[keys, values] syntax
34624                    let keys = self.parse_expression()?;
34625                    self.expect(TokenType::Comma)?;
34626                    let values = self.parse_expression()?;
34627                    self.expect(TokenType::RBracket)?;
34628                    expr = Expression::Function(Box::new(Function {
34629                        name: "MAP".to_string(),
34630                        args: vec![keys, values],
34631                        distinct: false,
34632                        trailing_comments: Vec::new(),
34633                        use_bracket_syntax: true,
34634                        no_parens: false,
34635                        quoted: false,
34636                        span: None,
34637                        inferred_type: None,
34638                    }));
34639                    continue;
34640                }
34641
34642                // Check for slice syntax: [start:end:step]
34643                // Handle [:...] case where start is omitted
34644                if self.check(TokenType::Colon) {
34645                    self.skip(); // consume first :
34646                                    // Parse end - use parse_slice_element to avoid : being interpreted as parameter
34647                    let end = self.parse_slice_element()?;
34648                    // Check for step (second colon)
34649                    let step = if self.match_token(TokenType::Colon) {
34650                        self.parse_slice_element()?
34651                    } else {
34652                        None
34653                    };
34654                    self.expect(TokenType::RBracket)?;
34655                    if step.is_some() {
34656                        // Three-part slice with step: Subscript with Slice index
34657                        let slice = Expression::Slice(Box::new(Slice {
34658                            this: None, // start is omitted
34659                            expression: end.map(Box::new),
34660                            step: step.map(Box::new),
34661                        }));
34662                        expr = Expression::Subscript(Box::new(Subscript {
34663                            this: expr,
34664                            index: slice,
34665                        }));
34666                    } else {
34667                        expr = Expression::ArraySlice(Box::new(ArraySlice {
34668                            this: expr,
34669                            start: None,
34670                            end,
34671                        }));
34672                    }
34673                } else {
34674                    let start = self.parse_slice_element()?;
34675                    // Check if this is a slice
34676                    if self.match_token(TokenType::Colon) {
34677                        let end = self.parse_slice_element()?;
34678                        // Check for step (second colon)
34679                        let step = if self.match_token(TokenType::Colon) {
34680                            self.parse_slice_element()?
34681                        } else {
34682                            None
34683                        };
34684                        self.expect(TokenType::RBracket)?;
34685                        if step.is_some() {
34686                            // Three-part slice with step: Subscript with Slice index
34687                            let slice = Expression::Slice(Box::new(Slice {
34688                                this: start.map(Box::new),
34689                                expression: end.map(Box::new),
34690                                step: step.map(Box::new),
34691                            }));
34692                            expr = Expression::Subscript(Box::new(Subscript {
34693                                this: expr,
34694                                index: slice,
34695                            }));
34696                        } else {
34697                            expr = Expression::ArraySlice(Box::new(ArraySlice {
34698                                this: expr,
34699                                start,
34700                                end,
34701                            }));
34702                        }
34703                    } else {
34704                        self.expect(TokenType::RBracket)?;
34705                        // Simple subscript access - start must be Some
34706                        let index =
34707                            start.unwrap_or_else(|| Expression::Null(crate::expressions::Null));
34708                        expr = Expression::Subscript(Box::new(Subscript { this: expr, index }));
34709                    }
34710                }
34711            } else if self.match_token(TokenType::DotColon) {
34712                let data_type = self.parse_data_type()?;
34713                expr = Expression::JSONCast(Box::new(JSONCast {
34714                    this: Box::new(expr),
34715                    to: data_type,
34716                }));
34717            } else if self.match_token(TokenType::Dot) {
34718                // Handle chained dot access (a.b.c.d)
34719                if self.match_token(TokenType::Star) {
34720                    // expr.* - struct field expansion with potential modifiers (EXCEPT, REPLACE, etc.)
34721                    let table_name = match &expr {
34722                        Expression::Column(col) => {
34723                            if let Some(ref table) = col.table {
34724                                Some(Identifier::new(format!("{}.{}", table.name, col.name.name)))
34725                            } else {
34726                                Some(col.name.clone())
34727                            }
34728                        }
34729                        Expression::Dot(d) => {
34730                            fn dot_to_name_inner(expr: &Expression) -> String {
34731                                match expr {
34732                                    Expression::Column(col) => {
34733                                        if let Some(ref table) = col.table {
34734                                            format!("{}.{}", table.name, col.name.name)
34735                                        } else {
34736                                            col.name.name.clone()
34737                                        }
34738                                    }
34739                                    Expression::Dot(d) => {
34740                                        format!("{}.{}", dot_to_name_inner(&d.this), d.field.name)
34741                                    }
34742                                    _ => String::new(),
34743                                }
34744                            }
34745                            Some(Identifier::new(dot_to_name_inner(&Expression::Dot(
34746                                d.clone(),
34747                            ))))
34748                        }
34749                        _ => None,
34750                    };
34751                    if table_name.is_some() {
34752                        let star = self.parse_star_modifiers(table_name)?;
34753                        expr = Expression::Star(star);
34754                        // ClickHouse: a.* APPLY(func) EXCEPT(col) REPLACE(expr AS col) in any order
34755                        if matches!(
34756                            self.config.dialect,
34757                            Some(crate::dialects::DialectType::ClickHouse)
34758                        ) {
34759                            loop {
34760                                if self.check(TokenType::Apply) {
34761                                    self.skip();
34762                                    let apply_expr = if self.match_token(TokenType::LParen) {
34763                                        let e = self.parse_expression()?;
34764                                        self.expect(TokenType::RParen)?;
34765                                        e
34766                                    } else {
34767                                        self.parse_expression()?
34768                                    };
34769                                    expr = Expression::Apply(Box::new(crate::expressions::Apply {
34770                                        this: Box::new(expr),
34771                                        expression: Box::new(apply_expr),
34772                                    }));
34773                                } else if self.check(TokenType::Except)
34774                                    || self.check(TokenType::Exclude)
34775                                {
34776                                    self.skip();
34777                                    self.match_identifier("STRICT");
34778                                    if self.match_token(TokenType::LParen) {
34779                                        loop {
34780                                            if self.check(TokenType::RParen) {
34781                                                break;
34782                                            }
34783                                            let _ = self.parse_expression()?;
34784                                            if !self.match_token(TokenType::Comma) {
34785                                                break;
34786                                            }
34787                                        }
34788                                        self.expect(TokenType::RParen)?;
34789                                    } else if self.is_identifier_token()
34790                                        || self.is_safe_keyword_as_identifier()
34791                                    {
34792                                        let _ = self.parse_expression()?;
34793                                    }
34794                                } else if self.check(TokenType::Replace) {
34795                                    self.skip();
34796                                    self.match_identifier("STRICT");
34797                                    if self.match_token(TokenType::LParen) {
34798                                        loop {
34799                                            if self.check(TokenType::RParen) {
34800                                                break;
34801                                            }
34802                                            let _ = self.parse_expression()?;
34803                                            if self.match_token(TokenType::As) {
34804                                                if self.is_identifier_token()
34805                                                    || self.is_safe_keyword_as_identifier()
34806                                                {
34807                                                    self.skip();
34808                                                }
34809                                            }
34810                                            if !self.match_token(TokenType::Comma) {
34811                                                break;
34812                                            }
34813                                        }
34814                                        self.expect(TokenType::RParen)?;
34815                                    } else {
34816                                        let _ = self.parse_expression()?;
34817                                        if self.match_token(TokenType::As) {
34818                                            if self.is_identifier_token()
34819                                                || self.is_safe_keyword_as_identifier()
34820                                            {
34821                                                self.skip();
34822                                            }
34823                                        }
34824                                    }
34825                                } else {
34826                                    break;
34827                                }
34828                            }
34829                        }
34830                    } else {
34831                        // For complex expressions (like CAST, function calls), use Dot with * as field
34832                        expr = Expression::Dot(Box::new(DotAccess {
34833                            this: expr,
34834                            field: Identifier::new("*"),
34835                        }));
34836                    }
34837                } else if self.check(TokenType::Identifier)
34838                    || self.check(TokenType::Var)
34839                    || self.check(TokenType::QuotedIdentifier)
34840                    || self.check_keyword()
34841                {
34842                    let is_quoted = self.check(TokenType::QuotedIdentifier);
34843                    let field_name = self.advance().text;
34844                    // Check if this is a method call (field followed by parentheses)
34845                    if self.check(TokenType::LParen) && !is_quoted {
34846                        // This is a method call like a.b.C() or x.EXTRACT()
34847                        self.skip(); // consume (
34848                        let args = if self.check(TokenType::RParen) {
34849                            Vec::new()
34850                        } else {
34851                            self.parse_expression_list()?
34852                        };
34853                        self.expect(TokenType::RParen)?;
34854                        // Create a method call expression (DotAccess with function call)
34855                        expr = Expression::MethodCall(Box::new(MethodCall {
34856                            this: expr,
34857                            method: Identifier::new(field_name),
34858                            args,
34859                        }));
34860                    } else {
34861                        let mut ident = Identifier::new(field_name);
34862                        if is_quoted {
34863                            ident.quoted = true;
34864                        }
34865                        expr = Expression::Dot(Box::new(DotAccess {
34866                            this: expr,
34867                            field: ident,
34868                        }));
34869                    }
34870                } else if self.check(TokenType::Number) {
34871                    // Handle numeric field access like a.0 or x.1
34872                    let field_name = self.advance().text;
34873                    expr = Expression::Dot(Box::new(DotAccess {
34874                        this: expr,
34875                        field: Identifier::new(field_name),
34876                    }));
34877                } else if matches!(
34878                    self.config.dialect,
34879                    Some(crate::dialects::DialectType::ClickHouse)
34880                ) && self.check(TokenType::Caret)
34881                {
34882                    // ClickHouse: json.^path — the ^ prefix means "get all nested subcolumns"
34883                    self.skip(); // consume ^
34884                                    // What follows should be an identifier path
34885                    let mut field_name = "^".to_string();
34886                    if self.check(TokenType::Identifier)
34887                        || self.check(TokenType::Var)
34888                        || self.check_keyword()
34889                    {
34890                        field_name.push_str(&self.advance().text);
34891                    }
34892                    expr = Expression::Dot(Box::new(DotAccess {
34893                        this: expr,
34894                        field: Identifier::new(field_name),
34895                    }));
34896                } else if matches!(
34897                    self.config.dialect,
34898                    Some(crate::dialects::DialectType::ClickHouse)
34899                ) && self.check(TokenType::Colon)
34900                {
34901                    // ClickHouse: json.path.:Type — the : prefix means type cast on JSON path
34902                    self.skip(); // consume :
34903                                    // Consume the type name
34904                    let mut type_name = ":".to_string();
34905                    if self.check(TokenType::Identifier)
34906                        || self.check(TokenType::Var)
34907                        || self.check_keyword()
34908                    {
34909                        type_name.push_str(&self.advance().text);
34910                    }
34911                    expr = Expression::Dot(Box::new(DotAccess {
34912                        this: expr,
34913                        field: Identifier::new(type_name),
34914                    }));
34915                } else if matches!(
34916                    self.config.dialect,
34917                    Some(crate::dialects::DialectType::ClickHouse)
34918                ) && self.check(TokenType::Dash)
34919                    && self
34920                        .peek_nth(1)
34921                        .is_some_and(|t| t.token_type == TokenType::Number)
34922                {
34923                    // ClickHouse: tuple.-1 — negative tuple index
34924                    self.skip(); // consume -
34925                    let num = self.advance().text;
34926                    expr = Expression::Dot(Box::new(DotAccess {
34927                        this: expr,
34928                        field: Identifier::new(format!("-{}", num)),
34929                    }));
34930                } else {
34931                    return Err(self.parse_error("Expected field name after dot"));
34932                }
34933            } else if self.match_token(TokenType::Collate) {
34934                // Parse COLLATE 'collation_name' or COLLATE "collation_name" or COLLATE collation_name
34935                let (collation, quoted, double_quoted) = if self.check(TokenType::String) {
34936                    // Single-quoted string: COLLATE 'de_DE'
34937                    (self.advance().text, true, false)
34938                } else if self.check(TokenType::QuotedIdentifier) {
34939                    // Double-quoted identifier: COLLATE "de_DE"
34940                    (self.advance().text, false, true)
34941                } else {
34942                    // Unquoted identifier: COLLATE de_DE
34943                    (self.expect_identifier_or_keyword()?, false, false)
34944                };
34945                expr = Expression::Collation(Box::new(CollationExpr {
34946                    this: expr,
34947                    collation,
34948                    quoted,
34949                    double_quoted,
34950                }));
34951            } else if self.check(TokenType::DColon)
34952                || self.check(TokenType::DColonDollar)
34953                || self.check(TokenType::DColonPercent)
34954                || self.check(TokenType::DColonQMark)
34955            {
34956                // For SingleStore, :: variants are JSON path extraction
34957                // For other dialects, :: is cast syntax (PostgreSQL-style)
34958                if matches!(
34959                    self.config.dialect,
34960                    Some(crate::dialects::DialectType::SingleStore)
34961                ) {
34962                    // SingleStore JSON path extraction: expr::key, expr::$key, expr::%key, expr::?key
34963                    if self.match_token(TokenType::DColon) {
34964                        // ::key -> JSON_EXTRACT_JSON(expr, 'key')
34965                        let path_key =
34966                            if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
34967                                self.advance().text
34968                            } else if self.check(TokenType::Number) {
34969                                self.advance().text
34970                            } else if self.check(TokenType::QuotedIdentifier) {
34971                                self.advance().text
34972                            } else {
34973                                return Err(self.parse_error(
34974                                    "Expected identifier or number after :: in JSON path",
34975                                ));
34976                            };
34977                        expr = Expression::Function(Box::new(Function::new(
34978                            "JSON_EXTRACT_JSON".to_string(),
34979                            vec![expr, Expression::string(&path_key)],
34980                        )));
34981                    } else if self.match_token(TokenType::DColonDollar) {
34982                        // ::$key -> JSON_EXTRACT_STRING(expr, 'key')
34983                        let path_key =
34984                            if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
34985                                self.advance().text
34986                            } else if self.check(TokenType::Number) {
34987                                self.advance().text
34988                            } else {
34989                                return Err(self.parse_error(
34990                                    "Expected identifier or number after ::$ in JSON path",
34991                                ));
34992                            };
34993                        expr = Expression::Function(Box::new(Function::new(
34994                            "JSON_EXTRACT_STRING".to_string(),
34995                            vec![expr, Expression::string(&path_key)],
34996                        )));
34997                    } else if self.match_token(TokenType::DColonPercent) {
34998                        // ::%key -> JSON_EXTRACT_DOUBLE(expr, 'key')
34999                        let path_key =
35000                            if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
35001                                self.advance().text
35002                            } else if self.check(TokenType::Number) {
35003                                self.advance().text
35004                            } else {
35005                                return Err(self.parse_error(
35006                                    "Expected identifier or number after ::% in JSON path",
35007                                ));
35008                            };
35009                        expr = Expression::Function(Box::new(Function::new(
35010                            "JSON_EXTRACT_DOUBLE".to_string(),
35011                            vec![expr, Expression::string(&path_key)],
35012                        )));
35013                    } else if self.match_token(TokenType::DColonQMark) {
35014                        // ::?key -> SingleStoreJsonPathQMark function (for JSON_MATCH_ANY patterns)
35015                        let path_key =
35016                            if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
35017                                self.advance().text
35018                            } else if self.check(TokenType::Number) {
35019                                self.advance().text
35020                            } else {
35021                                return Err(self.parse_error(
35022                                    "Expected identifier or number after ::? in JSON path",
35023                                ));
35024                            };
35025                        // Use a special function name that SingleStore generator will recognize
35026                        expr = Expression::Function(Box::new(Function::new(
35027                            "__SS_JSON_PATH_QMARK__".to_string(),
35028                            vec![expr, Expression::string(&path_key)],
35029                        )));
35030                    }
35031                } else {
35032                    // PostgreSQL :: cast operator: expr::type
35033                    self.skip(); // consume DColon
35034                                    // Use parse_data_type_for_cast to avoid consuming subscripts as array dimensions
35035                    let data_type = self.parse_data_type_for_cast()?;
35036                    expr = Expression::Cast(Box::new(Cast {
35037                        this: expr,
35038                        to: data_type,
35039                        trailing_comments: Vec::new(),
35040                        double_colon_syntax: true,
35041                        format: None,
35042                        default: None,
35043                        inferred_type: None,
35044                    }));
35045                }
35046            } else if self.match_token(TokenType::ColonGt) {
35047                // SingleStore :> cast operator: expr :> type
35048                let data_type = self.parse_data_type_for_cast()?;
35049                expr = Expression::Cast(Box::new(Cast {
35050                    this: expr,
35051                    to: data_type,
35052                    trailing_comments: Vec::new(),
35053                    double_colon_syntax: false, // Use :> syntax in generator
35054                    format: None,
35055                    default: None,
35056                    inferred_type: None,
35057                }));
35058            } else if self.match_token(TokenType::NColonGt) {
35059                // SingleStore !:> try cast operator: expr !:> type
35060                let data_type = self.parse_data_type_for_cast()?;
35061                expr = Expression::TryCast(Box::new(Cast {
35062                    this: expr,
35063                    to: data_type,
35064                    trailing_comments: Vec::new(),
35065                    double_colon_syntax: false,
35066                    format: None,
35067                    default: None,
35068                    inferred_type: None,
35069                }));
35070            } else if self.match_token(TokenType::QDColon) {
35071                // Databricks ?:: try cast operator: expr?::type
35072                let data_type = self.parse_data_type_for_cast()?;
35073                expr = Expression::TryCast(Box::new(Cast {
35074                    this: expr,
35075                    to: data_type,
35076                    trailing_comments: Vec::new(),
35077                    double_colon_syntax: true, // Uses :: style syntax
35078                    format: None,
35079                    default: None,
35080                    inferred_type: None,
35081                }));
35082            } else if self.check(TokenType::Arrow)
35083                && !matches!(
35084                    self.config.dialect,
35085                    Some(crate::dialects::DialectType::ClickHouse)
35086                )
35087            {
35088                self.skip(); // consume ->
35089                                // JSON extract operator: expr -> path (PostgreSQL, MySQL, DuckDB)
35090                                // Use parse_json_path_operand to get only the immediate operand for proper left-to-right associativity
35091                let path = self.parse_json_path_operand()?;
35092                expr = Expression::JsonExtract(Box::new(JsonExtractFunc {
35093                    this: expr,
35094                    path,
35095                    returning: None,
35096                    arrow_syntax: true,
35097                    hash_arrow_syntax: false,
35098                    wrapper_option: None,
35099                    quotes_option: None,
35100                    on_scalar_string: false,
35101                    on_error: None,
35102                }));
35103            } else if self.match_token(TokenType::DArrow) {
35104                // JSON extract text operator: expr ->> path (PostgreSQL, MySQL, DuckDB)
35105                // Use parse_json_path_operand to get only the immediate operand for proper left-to-right associativity
35106                let path = self.parse_json_path_operand()?;
35107                expr = Expression::JsonExtractScalar(Box::new(JsonExtractFunc {
35108                    this: expr,
35109                    path,
35110                    returning: None,
35111                    arrow_syntax: true,
35112                    hash_arrow_syntax: false,
35113                    wrapper_option: None,
35114                    quotes_option: None,
35115                    on_scalar_string: false,
35116                    on_error: None,
35117                }));
35118            } else if self.match_token(TokenType::HashArrow) {
35119                // JSONB path extract: expr #> path (PostgreSQL)
35120                // Use parse_json_path_operand to get only the immediate operand for proper left-to-right associativity
35121                let path = self.parse_json_path_operand()?;
35122                expr = Expression::JsonExtractPath(Box::new(JsonPathFunc {
35123                    this: expr,
35124                    paths: vec![path],
35125                }));
35126            } else if self.match_token(TokenType::DHashArrow) {
35127                // JSONB path extract text: expr #>> path (PostgreSQL)
35128                // For now, use JsonExtractScalar since the result is text
35129                // Use parse_json_path_operand to get only the immediate operand for proper left-to-right associativity
35130                let path = self.parse_json_path_operand()?;
35131                expr = Expression::JsonExtractScalar(Box::new(JsonExtractFunc {
35132                    this: expr,
35133                    path,
35134                    returning: None,
35135                    arrow_syntax: false,     // This is #>> not ->>
35136                    hash_arrow_syntax: true, // Mark as #>> operator
35137                    wrapper_option: None,
35138                    quotes_option: None,
35139                    on_scalar_string: false,
35140                    on_error: None,
35141                }));
35142            } else if self.check_join_marker() {
35143                // Oracle/Redshift-style outer join marker: column (+)
35144                // Only applies to Column expressions
35145                if let Expression::Column(col) = &mut expr {
35146                    self.skip(); // consume (
35147                    self.skip(); // consume +
35148                    self.skip(); // consume )
35149                    col.join_mark = true;
35150                    // Don't continue - join marker is terminal (no more postfix ops after it)
35151                    break;
35152                }
35153                // If not a Column, just break - the marker is invalid in this context
35154                else {
35155                    break;
35156                }
35157            } else {
35158                break;
35159            }
35160        }
35161        Ok(expr)
35162    }
35163
35164    /// Check if the next tokens are the Oracle-style join marker (+)
35165    fn check_join_marker(&self) -> bool {
35166        self.check(TokenType::LParen)
35167            && self
35168                .peek_nth(1)
35169                .map_or(false, |t| t.token_type == TokenType::Plus)
35170            && self
35171                .peek_nth(2)
35172                .map_or(false, |t| t.token_type == TokenType::RParen)
35173    }
35174
35175    /// Parse OVER clause
35176    fn parse_over_clause(&mut self) -> Result<Over> {
35177        // Handle OVER window_name (without parentheses)
35178        if !self.check(TokenType::LParen) {
35179            // OVER window_name - just a named window reference
35180            let window_name = self.expect_identifier_or_keyword()?;
35181            return Ok(Over {
35182                window_name: Some(Identifier::new(window_name)),
35183                partition_by: Vec::new(),
35184                order_by: Vec::new(),
35185                frame: None,
35186                alias: None,
35187            });
35188        }
35189
35190        self.expect(TokenType::LParen)?;
35191
35192        // Check for named window reference at start of OVER clause
35193        // e.g., OVER (w ORDER BY y) - w is a window name that can be extended
35194        let window_name = if (self.check(TokenType::Identifier)
35195            || self.check(TokenType::Var)
35196            || self.check_keyword())
35197            && !self.check(TokenType::Partition)
35198            && !self.check(TokenType::Order)
35199            && !self.check(TokenType::Rows)
35200            && !self.check(TokenType::Range)
35201            && !self.check(TokenType::Groups)
35202            && !self.check(TokenType::Distribute)
35203            && !self.check(TokenType::Sort)
35204        {
35205            // Look ahead to see if next token indicates this is a window name
35206            let pos = self.current;
35207            let name = self.advance().text;
35208            // If next token is a keyword that can follow a window name, this is a named reference
35209            if self.check(TokenType::Order)
35210                || self.check(TokenType::Partition)
35211                || self.check(TokenType::Rows)
35212                || self.check(TokenType::Range)
35213                || self.check(TokenType::Groups)
35214                || self.check(TokenType::RParen)
35215                || self.check(TokenType::Distribute)
35216                || self.check(TokenType::Sort)
35217            {
35218                Some(Identifier::new(name))
35219            } else {
35220                // Not a named window, restore position
35221                self.current = pos;
35222                None
35223            }
35224        } else {
35225            None
35226        };
35227
35228        // Parse PARTITION BY or DISTRIBUTE BY (Hive uses DISTRIBUTE BY in window specs)
35229        let partition_by = if self.match_keywords(&[TokenType::Partition, TokenType::By]) {
35230            self.parse_expression_list()?
35231        } else if self.match_keywords(&[TokenType::Distribute, TokenType::By]) {
35232            // Hive: DISTRIBUTE BY is equivalent to PARTITION BY in window specs
35233            self.parse_expression_list()?
35234        } else {
35235            Vec::new()
35236        };
35237
35238        // Parse ORDER BY or SORT BY (Hive uses SORT BY in window specs)
35239        let order_by = if self.match_keywords(&[TokenType::Order, TokenType::By])
35240            || self.match_keywords(&[TokenType::Sort, TokenType::By])
35241        {
35242            let mut exprs = Vec::new();
35243            loop {
35244                let expr = self.parse_expression()?;
35245                let (desc, explicit_asc) = if self.match_token(TokenType::Desc) {
35246                    (true, false)
35247                } else if self.match_token(TokenType::Asc) {
35248                    (false, true)
35249                } else {
35250                    (false, false)
35251                };
35252                // ClickHouse/SQL: COLLATE 'collation' in window ORDER BY
35253                if self.match_token(TokenType::Collate) {
35254                    // Consume collation name (string or identifier)
35255                    if self.check(TokenType::String) {
35256                        self.skip();
35257                    } else if self.check(TokenType::QuotedIdentifier) {
35258                        self.skip();
35259                    } else {
35260                        let _ = self.expect_identifier_or_keyword();
35261                    }
35262                }
35263                let nulls_first = if self.match_token(TokenType::Nulls) {
35264                    if self.match_token(TokenType::First) {
35265                        Some(true)
35266                    } else if self.match_token(TokenType::Last) {
35267                        Some(false)
35268                    } else {
35269                        return Err(self.parse_error("Expected FIRST or LAST after NULLS"));
35270                    }
35271                } else {
35272                    None
35273                };
35274                // ClickHouse: WITH FILL in window ORDER BY
35275                let with_fill = if matches!(
35276                    self.config.dialect,
35277                    Some(crate::dialects::DialectType::ClickHouse)
35278                ) && self.check(TokenType::With)
35279                    && self.current + 1 < self.tokens.len()
35280                    && self.tokens[self.current + 1]
35281                        .text
35282                        .eq_ignore_ascii_case("FILL")
35283                {
35284                    self.skip(); // consume WITH
35285                    self.skip(); // consume FILL
35286                    let from_ = if self.match_token(TokenType::From) {
35287                        Some(Box::new(self.parse_or()?))
35288                    } else {
35289                        None
35290                    };
35291                    let to = if self.match_text_seq(&["TO"]) {
35292                        Some(Box::new(self.parse_or()?))
35293                    } else {
35294                        None
35295                    };
35296                    let step = if self.match_text_seq(&["STEP"]) {
35297                        Some(Box::new(self.parse_or()?))
35298                    } else {
35299                        None
35300                    };
35301                    let staleness = if self.match_text_seq(&["STALENESS"]) {
35302                        Some(Box::new(self.parse_or()?))
35303                    } else {
35304                        None
35305                    };
35306                    let interpolate = if self.match_text_seq(&["INTERPOLATE"]) {
35307                        if self.match_token(TokenType::LParen) {
35308                            let items = self.parse_expression_list()?;
35309                            self.expect(TokenType::RParen)?;
35310                            if items.len() == 1 {
35311                                Some(Box::new(items.into_iter().next().unwrap()))
35312                            } else {
35313                                Some(Box::new(Expression::Tuple(Box::new(
35314                                    crate::expressions::Tuple { expressions: items },
35315                                ))))
35316                            }
35317                        } else {
35318                            None
35319                        }
35320                    } else {
35321                        None
35322                    };
35323                    Some(Box::new(WithFill {
35324                        from_,
35325                        to,
35326                        step,
35327                        staleness,
35328                        interpolate,
35329                    }))
35330                } else {
35331                    None
35332                };
35333                exprs.push(Ordered {
35334                    this: expr,
35335                    desc,
35336                    nulls_first,
35337                    explicit_asc,
35338                    with_fill,
35339                });
35340                if !self.match_token(TokenType::Comma) {
35341                    break;
35342                }
35343            }
35344            exprs
35345        } else {
35346            Vec::new()
35347        };
35348
35349        // Parse window frame
35350        let frame = self.parse_window_frame()?;
35351
35352        self.expect(TokenType::RParen)?;
35353
35354        Ok(Over {
35355            window_name,
35356            partition_by,
35357            order_by,
35358            frame,
35359            alias: None,
35360        })
35361    }
35362
35363    /// Parse window frame specification (ROWS/RANGE/GROUPS BETWEEN ...)
35364    fn parse_window_frame(&mut self) -> Result<Option<WindowFrame>> {
35365        let (kind, kind_text) = if self.match_token(TokenType::Rows) {
35366            (
35367                WindowFrameKind::Rows,
35368                self.tokens[self.current - 1].text.clone(),
35369            )
35370        } else if self.match_token(TokenType::Range) {
35371            (
35372                WindowFrameKind::Range,
35373                self.tokens[self.current - 1].text.clone(),
35374            )
35375        } else if self.match_token(TokenType::Groups) {
35376            (
35377                WindowFrameKind::Groups,
35378                self.tokens[self.current - 1].text.clone(),
35379            )
35380        } else {
35381            return Ok(None);
35382        };
35383
35384        // Parse BETWEEN or single bound
35385        let (start, start_side_text, end, end_side_text) = if self.match_token(TokenType::Between) {
35386            let (start, st) = self.parse_window_frame_bound()?;
35387            self.expect(TokenType::And)?;
35388            let (end, et) = self.parse_window_frame_bound()?;
35389            (start, st, Some(end), et)
35390        } else {
35391            let (start, st) = self.parse_window_frame_bound()?;
35392            (start, st, None, None)
35393        };
35394
35395        // Parse optional EXCLUDE clause
35396        let exclude = if self.match_token(TokenType::Exclude) {
35397            if self.match_token(TokenType::Current) {
35398                self.expect(TokenType::Row)?;
35399                Some(WindowFrameExclude::CurrentRow)
35400            } else if self.match_token(TokenType::Group) {
35401                Some(WindowFrameExclude::Group)
35402            } else if self.match_token(TokenType::Ties) {
35403                Some(WindowFrameExclude::Ties)
35404            } else if self.match_token(TokenType::No) {
35405                self.expect(TokenType::Others)?;
35406                Some(WindowFrameExclude::NoOthers)
35407            } else {
35408                return Err(self
35409                    .parse_error("Expected CURRENT ROW, GROUP, TIES, or NO OTHERS after EXCLUDE"));
35410            }
35411        } else {
35412            None
35413        };
35414
35415        Ok(Some(WindowFrame {
35416            kind,
35417            start,
35418            end,
35419            exclude,
35420            kind_text: Some(kind_text),
35421            start_side_text,
35422            end_side_text,
35423        }))
35424    }
35425
35426    /// Parse a window frame bound, returning the bound and the original text of the side keyword
35427    fn parse_window_frame_bound(&mut self) -> Result<(WindowFrameBound, Option<String>)> {
35428        if self.match_token(TokenType::Current) {
35429            self.expect(TokenType::Row)?;
35430            Ok((WindowFrameBound::CurrentRow, None))
35431        } else if self.match_token(TokenType::Unbounded) {
35432            if self.match_token(TokenType::Preceding) {
35433                let text = self.tokens[self.current - 1].text.clone();
35434                Ok((WindowFrameBound::UnboundedPreceding, Some(text)))
35435            } else if self.match_token(TokenType::Following) {
35436                let text = self.tokens[self.current - 1].text.clone();
35437                Ok((WindowFrameBound::UnboundedFollowing, Some(text)))
35438            } else {
35439                Err(self.parse_error("Expected PRECEDING or FOLLOWING after UNBOUNDED"))
35440            }
35441        } else if self.match_token(TokenType::Preceding) {
35442            let text = self.tokens[self.current - 1].text.clone();
35443            // PRECEDING [value] (inverted syntax for some dialects)
35444            // If no value follows (e.g., just "PRECEDING" or "PRECEDING)"), use BarePreceding
35445            if self.check(TokenType::RParen) || self.check(TokenType::Comma) {
35446                Ok((WindowFrameBound::BarePreceding, Some(text)))
35447            } else {
35448                let expr = self.parse_primary()?;
35449                Ok((WindowFrameBound::Preceding(Box::new(expr)), Some(text)))
35450            }
35451        } else if self.match_token(TokenType::Following) {
35452            let text = self.tokens[self.current - 1].text.clone();
35453            // FOLLOWING [value] (inverted syntax for some dialects)
35454            // If no value follows (e.g., just "FOLLOWING" or "FOLLOWING)"), use BareFollowing
35455            if self.check(TokenType::RParen) || self.check(TokenType::Comma) {
35456                Ok((WindowFrameBound::BareFollowing, Some(text)))
35457            } else {
35458                let expr = self.parse_primary()?;
35459                Ok((WindowFrameBound::Following(Box::new(expr)), Some(text)))
35460            }
35461        } else {
35462            // <expr> PRECEDING | FOLLOWING (standard syntax)
35463            // Use parse_addition to handle expressions like 1 + 1 PRECEDING
35464            let expr = self.parse_addition()?;
35465            if self.match_token(TokenType::Preceding) {
35466                let text = self.tokens[self.current - 1].text.clone();
35467                Ok((WindowFrameBound::Preceding(Box::new(expr)), Some(text)))
35468            } else if self.match_token(TokenType::Following) {
35469                let text = self.tokens[self.current - 1].text.clone();
35470                Ok((WindowFrameBound::Following(Box::new(expr)), Some(text)))
35471            } else {
35472                // Bare numeric bounds without PRECEDING/FOLLOWING
35473                // (e.g., RANGE BETWEEN 1 AND 3)
35474                Ok((WindowFrameBound::Value(Box::new(expr)), None))
35475            }
35476        }
35477    }
35478
35479    /// Try to parse INTERVAL expression. Returns None if INTERVAL should be treated as identifier.
35480    fn try_parse_interval(&mut self) -> Result<Option<Expression>> {
35481        self.try_parse_interval_internal(true)
35482    }
35483
35484    /// Internal interval parsing that optionally matches the INTERVAL keyword.
35485    /// When match_interval is false, it parses a chained interval value-unit pair
35486    /// without requiring the INTERVAL keyword.
35487    fn try_parse_interval_internal(&mut self, match_interval: bool) -> Result<Option<Expression>> {
35488        let start_pos = self.current;
35489
35490        // Consume the INTERVAL keyword if required
35491        if match_interval {
35492            if !self.check(TokenType::Interval) {
35493                return Ok(None);
35494            }
35495            self.expect(TokenType::Interval)?;
35496
35497            // Check if next token is an operator - if so, INTERVAL is used as identifier
35498            if self.check(TokenType::Eq)
35499                || self.check(TokenType::Neq)
35500                || self.check(TokenType::Lt)
35501                || self.check(TokenType::Gt)
35502                || self.check(TokenType::Lte)
35503                || self.check(TokenType::Gte)
35504                || self.check(TokenType::And)
35505                || self.check(TokenType::Or)
35506                || self.check(TokenType::Is)
35507                || self.check(TokenType::In)
35508                || self.check(TokenType::Like)
35509                || self.check(TokenType::ILike)
35510                || self.check(TokenType::Between)
35511                || self.check(TokenType::Then)
35512                || self.check(TokenType::Else)
35513                || self.check(TokenType::When)
35514                || self.check(TokenType::End)
35515                || self.check(TokenType::Comma)
35516                || self.check(TokenType::RParen)
35517                || self.check(TokenType::DColon)
35518            {
35519                // INTERVAL is used as identifier
35520                self.current = start_pos;
35521                return Ok(None);
35522            }
35523        }
35524
35525        // Parse the value after INTERVAL
35526        // IMPORTANT: For string literals, don't use parse_primary() because it calls
35527        // maybe_parse_subscript() which would consume postfix operators like ::TYPE.
35528        // Those should be applied to the full INTERVAL expression, not just the value inside.
35529        // e.g., INTERVAL '1 hour'::VARCHAR should be CAST(INTERVAL '1 hour' AS VARCHAR)
35530        //       not INTERVAL CAST('1 hour' AS VARCHAR)
35531        // For non-string values, use parse_addition() to handle expressions like
35532        // INTERVAL 2 * 2 MONTH or INTERVAL DAYOFMONTH(dt) - 1 DAY (MySQL syntax)
35533        // This matches Python sqlglot's _parse_term() behavior which handles +, -, *, /, %
35534        let value = if self.check(TokenType::String) {
35535            let token = self.advance();
35536            Some(Expression::Literal(Literal::String(token.text)))
35537        } else if !self.is_at_end() && !self.is_statement_terminator() {
35538            Some(self.parse_addition()?)
35539        } else {
35540            None
35541        };
35542
35543        // Check if we should treat INTERVAL as an identifier instead
35544        // This happens when:
35545        // - No value was parsed, OR
35546        // - Value is an unqualified, unquoted column reference AND
35547        //   what follows is NOT a valid interval unit
35548        if let Some(ref val) = value {
35549            if let Expression::Column(col) = val {
35550                // Column without table qualifier
35551                if col.table.is_none() {
35552                    // Check if identifier is quoted
35553                    let is_quoted = col.name.quoted;
35554                    if !is_quoted {
35555                        // Check if next token is a valid interval unit
35556                        if !self.is_valid_interval_unit() && !self.check(TokenType::As) {
35557                            // Backtrack - INTERVAL is used as identifier
35558                            self.current = start_pos;
35559                            return Ok(None);
35560                        }
35561                    }
35562                }
35563            } else if let Expression::Identifier(id) = val {
35564                // Bare identifier without table qualifier
35565                let is_quoted = id.quoted;
35566                if !is_quoted {
35567                    // Check if next token is a valid interval unit
35568                    if !self.is_valid_interval_unit() && !self.check(TokenType::As) {
35569                        // Backtrack - INTERVAL is used as identifier
35570                        self.current = start_pos;
35571                        return Ok(None);
35572                    }
35573                }
35574            }
35575        } else if self.is_at_end() || self.is_statement_terminator() {
35576            // No value, and at end/terminator - INTERVAL is an identifier
35577            self.current = start_pos;
35578            return Ok(None);
35579        }
35580
35581        // Now parse the optional unit
35582        let mut unit = self.try_parse_interval_unit()?;
35583
35584        // Split compound interval strings like '1 day' into value '1' and unit DAY
35585        // This matches Python sqlglot's INTERVAL_STRING_RE behavior
35586        // Only apply in generic mode -- dialects like PostgreSQL preserve compound strings
35587        let is_generic = self.config.dialect.is_none()
35588            || matches!(
35589                self.config.dialect,
35590                Some(crate::dialects::DialectType::Generic)
35591            );
35592        let value = if unit.is_none() && is_generic {
35593            if let Some(Expression::Literal(Literal::String(ref s))) = value {
35594                let trimmed = s.trim();
35595                // Match pattern: optional negative sign, digits (optional decimal), space(s), alpha unit
35596                let mut split_pos = None;
35597                let mut found_space = false;
35598                let bytes = trimmed.as_bytes();
35599                let mut i = 0;
35600                // Skip optional negative sign
35601                if i < bytes.len() && bytes[i] == b'-' {
35602                    i += 1;
35603                }
35604                // Expect digits
35605                let digit_start = i;
35606                while i < bytes.len() && bytes[i].is_ascii_digit() {
35607                    i += 1;
35608                }
35609                if i > digit_start {
35610                    // Optional decimal part
35611                    if i < bytes.len() && bytes[i] == b'.' {
35612                        i += 1;
35613                        while i < bytes.len() && bytes[i].is_ascii_digit() {
35614                            i += 1;
35615                        }
35616                    }
35617                    // Expect whitespace
35618                    let space_start = i;
35619                    while i < bytes.len() && bytes[i].is_ascii_whitespace() {
35620                        i += 1;
35621                    }
35622                    if i > space_start {
35623                        found_space = true;
35624                        split_pos = Some(i);
35625                    }
35626                }
35627                if found_space {
35628                    if let Some(pos) = split_pos {
35629                        let unit_text = &trimmed[pos..];
35630                        // Verify it's all alpha
35631                        if !unit_text.is_empty()
35632                            && unit_text.chars().all(|c| c.is_ascii_alphabetic())
35633                        {
35634                            let num_part = trimmed[..pos].trim_end().to_string();
35635                            let unit_upper = unit_text.to_ascii_uppercase();
35636                            // Try to parse as interval unit
35637                            if let Some(parsed_unit) =
35638                                Self::parse_interval_unit_from_string(&unit_upper)
35639                            {
35640                                // Check if the original text had an 'S' suffix (plural)
35641                                let is_plural = unit_upper.ends_with('S');
35642                                unit = Some(IntervalUnitSpec::Simple {
35643                                    unit: parsed_unit,
35644                                    use_plural: is_plural,
35645                                });
35646                                Some(Expression::Literal(Literal::String(num_part)))
35647                            } else {
35648                                value
35649                            }
35650                        } else {
35651                            value
35652                        }
35653                    } else {
35654                        value
35655                    }
35656                } else {
35657                    value
35658                }
35659            } else {
35660                value
35661            }
35662        } else {
35663            value
35664        };
35665
35666        // Convert number literals to string literals in intervals (canonical form).
35667        // Most dialects support INTERVAL '5' DAY, so we normalize to this form
35668        // for easier transpilation. This matches Python sqlglot's behavior in
35669        // _parse_interval_span: "if this and this.is_number: this = exp.Literal.string(this.to_py())"
35670        let value = match value {
35671            Some(Expression::Literal(Literal::Number(n))) if unit.is_some() => {
35672                Some(Expression::Literal(Literal::String(n)))
35673            }
35674            other => other,
35675        };
35676
35677        let interval = Expression::Interval(Box::new(Interval { this: value, unit }));
35678
35679        // Support for chained multi-unit interval syntax (Spark/Hive):
35680        // INTERVAL '5' HOURS '30' MINUTES -> INTERVAL '5' HOURS + INTERVAL '30' MINUTES
35681        // This is done by optionally matching a PLUS sign, and if followed by
35682        // another string or number (without INTERVAL keyword), recursively parsing
35683        // and creating an Add expression.
35684        let before_plus = self.current;
35685        let has_plus = self.match_token(TokenType::Plus);
35686
35687        // Check if followed by a STRING or NUMBER (potential chained interval)
35688        if self.check(TokenType::String) || self.check(TokenType::Number) {
35689            // Recursively parse the chained interval without the INTERVAL keyword
35690            if let Some(next_interval) = self.try_parse_interval_internal(false)? {
35691                return Ok(Some(Expression::Add(Box::new(BinaryOp::new(
35692                    interval,
35693                    next_interval,
35694                )))));
35695            }
35696        }
35697
35698        // If we consumed a PLUS but didn't find a chained interval, backtrack
35699        if has_plus {
35700            self.current = before_plus;
35701        }
35702
35703        Ok(Some(interval))
35704    }
35705
35706    /// Check if current token is a valid interval unit
35707    fn is_valid_interval_unit(&self) -> bool {
35708        if self.is_at_end() {
35709            return false;
35710        }
35711        let text = self.peek().text.to_ascii_uppercase();
35712        matches!(
35713            text.as_str(),
35714            "YEAR"
35715                | "YEARS"
35716                | "MONTH"
35717                | "MONTHS"
35718                | "DAY"
35719                | "DAYS"
35720                | "HOUR"
35721                | "HOURS"
35722                | "MINUTE"
35723                | "MINUTES"
35724                | "SECOND"
35725                | "SECONDS"
35726                | "MILLISECOND"
35727                | "MILLISECONDS"
35728                | "MICROSECOND"
35729                | "MICROSECONDS"
35730                | "NANOSECOND"
35731                | "NANOSECONDS"
35732                | "WEEK"
35733                | "WEEKS"
35734                | "QUARTER"
35735                | "QUARTERS"
35736        )
35737    }
35738
35739    /// Check if current token terminates a statement/expression context
35740    fn is_statement_terminator(&self) -> bool {
35741        if self.is_at_end() {
35742            return true;
35743        }
35744        matches!(
35745            self.peek().token_type,
35746            TokenType::Semicolon
35747                | TokenType::RParen
35748                | TokenType::RBracket
35749                | TokenType::Comma
35750                | TokenType::From
35751                | TokenType::Where
35752                | TokenType::GroupBy
35753                | TokenType::Having
35754                | TokenType::OrderBy
35755                | TokenType::Limit
35756                | TokenType::Union
35757                | TokenType::Intersect
35758                | TokenType::Except
35759                | TokenType::End
35760                | TokenType::Then
35761                | TokenType::Else
35762                | TokenType::When
35763        )
35764    }
35765
35766    /// Try to parse interval unit - returns None if no unit present
35767    fn try_parse_interval_unit(&mut self) -> Result<Option<IntervalUnitSpec>> {
35768        // First, check if there's a function (like CURRENT_DATE, CAST(...))
35769        if self.is_function_start() {
35770            let func = self.parse_primary()?;
35771            return Ok(Some(IntervalUnitSpec::Expr(Box::new(func))));
35772        }
35773
35774        // Try to parse a simple unit or span
35775        if let Some((unit, use_plural)) = self.try_parse_simple_interval_unit()? {
35776            // Check for "TO" to make it a span (e.g., YEAR TO MONTH)
35777            // Use lookahead to avoid consuming TO when it's part of WITH FILL
35778            if self.check_keyword_text("TO") {
35779                let saved = self.current;
35780                self.skip(); // consume TO
35781                if let Some((end_unit, _)) = self.try_parse_simple_interval_unit()? {
35782                    return Ok(Some(IntervalUnitSpec::Span(IntervalSpan {
35783                        this: unit,
35784                        expression: end_unit,
35785                    })));
35786                } else {
35787                    // Not followed by a valid interval unit — backtrack
35788                    self.current = saved;
35789                }
35790            }
35791            return Ok(Some(IntervalUnitSpec::Simple { unit, use_plural }));
35792        }
35793
35794        // No unit found
35795        Ok(None)
35796    }
35797
35798    /// Parse an interval unit from a string (used for splitting compound interval strings)
35799    fn parse_interval_unit_from_string(s: &str) -> Option<IntervalUnit> {
35800        // Strip trailing 'S' for plural forms
35801        let base = if s.ends_with('S') && s.len() > 1 {
35802            &s[..s.len() - 1]
35803        } else {
35804            s
35805        };
35806        match base {
35807            "YEAR" => Some(IntervalUnit::Year),
35808            "MONTH" => Some(IntervalUnit::Month),
35809            "DAY" => Some(IntervalUnit::Day),
35810            "HOUR" => Some(IntervalUnit::Hour),
35811            "MINUTE" => Some(IntervalUnit::Minute),
35812            "SECOND" => Some(IntervalUnit::Second),
35813            "MILLISECOND" => Some(IntervalUnit::Millisecond),
35814            "MICROSECOND" => Some(IntervalUnit::Microsecond),
35815            "QUARTER" => Some(IntervalUnit::Quarter),
35816            "WEEK" => Some(IntervalUnit::Week),
35817            _ => None,
35818        }
35819    }
35820
35821    /// Try to parse a simple interval unit (YEAR, MONTH, etc.) - returns (unit, is_plural)
35822    fn try_parse_simple_interval_unit(&mut self) -> Result<Option<(IntervalUnit, bool)>> {
35823        if self.is_at_end() {
35824            return Ok(None);
35825        }
35826
35827        let text_upper = self.peek().text.to_ascii_uppercase();
35828        let result = match text_upper.as_str() {
35829            "YEAR" => Some((IntervalUnit::Year, false)),
35830            "YEARS" => Some((IntervalUnit::Year, true)),
35831            "MONTH" => Some((IntervalUnit::Month, false)),
35832            "MONTHS" => Some((IntervalUnit::Month, true)),
35833            "DAY" => Some((IntervalUnit::Day, false)),
35834            "DAYS" => Some((IntervalUnit::Day, true)),
35835            "HOUR" => Some((IntervalUnit::Hour, false)),
35836            "HOURS" => Some((IntervalUnit::Hour, true)),
35837            "MINUTE" => Some((IntervalUnit::Minute, false)),
35838            "MINUTES" => Some((IntervalUnit::Minute, true)),
35839            "SECOND" => Some((IntervalUnit::Second, false)),
35840            "SECONDS" => Some((IntervalUnit::Second, true)),
35841            "MILLISECOND" => Some((IntervalUnit::Millisecond, false)),
35842            "MILLISECONDS" => Some((IntervalUnit::Millisecond, true)),
35843            "MICROSECOND" => Some((IntervalUnit::Microsecond, false)),
35844            "MICROSECONDS" => Some((IntervalUnit::Microsecond, true)),
35845            "NANOSECOND" => Some((IntervalUnit::Nanosecond, false)),
35846            "NANOSECONDS" => Some((IntervalUnit::Nanosecond, true)),
35847            "QUARTER" => Some((IntervalUnit::Quarter, false)),
35848            "QUARTERS" => Some((IntervalUnit::Quarter, true)),
35849            "WEEK" => Some((IntervalUnit::Week, false)),
35850            "WEEKS" => Some((IntervalUnit::Week, true)),
35851            _ => None,
35852        };
35853
35854        if result.is_some() {
35855            self.skip(); // consume the unit token
35856        }
35857
35858        Ok(result)
35859    }
35860
35861    /// Check if current position starts a function call or no-paren function
35862    fn is_function_start(&self) -> bool {
35863        if self.is_at_end() {
35864            return false;
35865        }
35866        let token_type = self.peek().token_type;
35867
35868        // Check NO_PAREN_FUNCTIONS configuration map
35869        if NO_PAREN_FUNCTIONS.contains(&token_type) {
35870            if !matches!(
35871                self.config.dialect,
35872                Some(crate::dialects::DialectType::ClickHouse)
35873            ) || token_type != TokenType::CurrentTimestamp
35874            {
35875                return true;
35876            }
35877        }
35878
35879        // Cast functions are always functions
35880        if matches!(
35881            token_type,
35882            TokenType::Cast | TokenType::TryCast | TokenType::SafeCast
35883        ) {
35884            return true;
35885        }
35886
35887        // Check NO_PAREN_FUNCTION_NAMES for string-based lookup
35888        // (handles cases where functions are tokenized as Var/Identifier)
35889        let text_upper = self.peek().text.to_ascii_uppercase();
35890        if crate::function_registry::is_no_paren_function_name_upper(text_upper.as_str()) {
35891            if !matches!(
35892                self.config.dialect,
35893                Some(crate::dialects::DialectType::ClickHouse)
35894            ) || text_upper.as_str() != "CURRENT_TIMESTAMP"
35895            {
35896                return true;
35897            }
35898        }
35899
35900        // Identifier followed by left paren (function call)
35901        if self.is_identifier_token() && self.check_next(TokenType::LParen) {
35902            return true;
35903        }
35904
35905        false
35906    }
35907
35908    /// Try to parse Oracle interval span after an expression.
35909    /// Syntax: (expr) DAY[(precision)] TO SECOND[(fractional_precision)]
35910    /// This is used in Oracle for interval expressions like:
35911    /// (SYSTIMESTAMP - order_date) DAY(9) TO SECOND(3)
35912    fn try_parse_oracle_interval_span(&mut self, expr: Expression) -> Result<Expression> {
35913        let start_pos = self.current;
35914
35915        // Check if current token is an interval unit keyword (DAY, HOUR, MINUTE, SECOND, YEAR, MONTH)
35916        let start_unit_name = if !self.is_at_end() {
35917            let text = self.peek().text.to_ascii_uppercase();
35918            if matches!(
35919                text.as_str(),
35920                "DAY" | "HOUR" | "MINUTE" | "SECOND" | "YEAR" | "MONTH"
35921            ) {
35922                Some(text)
35923            } else {
35924                None
35925            }
35926        } else {
35927            None
35928        };
35929
35930        if start_unit_name.is_none() {
35931            return Ok(expr);
35932        }
35933
35934        let start_unit_name = start_unit_name.unwrap();
35935        self.skip(); // consume the unit keyword
35936
35937        // Parse optional precision: DAY(9) or just DAY
35938        let start_unit = if self.match_token(TokenType::LParen) {
35939            // Parse precision
35940            let precision = self.parse_expression()?;
35941            self.expect(TokenType::RParen)?;
35942            // Create a function-like expression for the unit with precision
35943            Expression::Anonymous(Box::new(Anonymous {
35944                this: Box::new(Expression::Identifier(Identifier {
35945                    name: start_unit_name.clone(),
35946                    quoted: false,
35947                    trailing_comments: Vec::new(),
35948                    span: None,
35949                })),
35950                expressions: vec![precision],
35951            }))
35952        } else {
35953            // Simple unit without precision
35954            Expression::Var(Box::new(Var {
35955                this: start_unit_name,
35956            }))
35957        };
35958
35959        // Check for TO keyword
35960        if !self.match_keyword("TO") {
35961            // Not an interval span, backtrack
35962            self.current = start_pos;
35963            return Ok(expr);
35964        }
35965
35966        // Parse end unit
35967        let end_unit_name = if !self.is_at_end() {
35968            let text = self.peek().text.to_ascii_uppercase();
35969            if matches!(
35970                text.as_str(),
35971                "DAY" | "HOUR" | "MINUTE" | "SECOND" | "YEAR" | "MONTH"
35972            ) {
35973                Some(text)
35974            } else {
35975                None
35976            }
35977        } else {
35978            None
35979        };
35980
35981        let end_unit_name = match end_unit_name {
35982            Some(name) => name,
35983            None => {
35984                // No valid end unit, backtrack
35985                self.current = start_pos;
35986                return Ok(expr);
35987            }
35988        };
35989
35990        self.skip(); // consume the end unit keyword
35991
35992        // Parse optional precision for end unit: SECOND(3) or just SECOND
35993        let end_unit = if self.match_token(TokenType::LParen) {
35994            // Parse fractional precision
35995            let precision = self.parse_expression()?;
35996            self.expect(TokenType::RParen)?;
35997            // Create a function-like expression for the unit with precision
35998            Expression::Anonymous(Box::new(Anonymous {
35999                this: Box::new(Expression::Identifier(Identifier {
36000                    name: end_unit_name.clone(),
36001                    quoted: false,
36002                    trailing_comments: Vec::new(),
36003                    span: None,
36004                })),
36005                expressions: vec![precision],
36006            }))
36007        } else {
36008            // Simple unit without precision
36009            Expression::Var(Box::new(Var {
36010                this: end_unit_name,
36011            }))
36012        };
36013
36014        // Create an Interval expression with ExprSpan unit
36015        Ok(Expression::Interval(Box::new(Interval {
36016            this: Some(expr),
36017            unit: Some(IntervalUnitSpec::ExprSpan(IntervalSpanExpr {
36018                this: Box::new(start_unit),
36019                expression: Box::new(end_unit),
36020            })),
36021        })))
36022    }
36023
36024    /// Check if the current position starts a typed column list (for table function aliases)
36025    /// like: (col1 type1, col2 type2)
36026    /// This peeks ahead to see if the first column name is followed by a type token,
36027    /// rather than a comma or closing paren (which would indicate simple column aliases).
36028    /// Used for PostgreSQL functions like JSON_TO_RECORDSET that have typed column definitions.
36029    fn check_typed_column_list(&self) -> bool {
36030        // We're positioned after '(' - check pattern: identifier type
36031        // If we see identifier followed by something that's not ',' or ')', it's typed
36032        if self.is_at_end() {
36033            return false;
36034        }
36035
36036        // Check if current is an identifier (column name)
36037        let has_identifier = self.check(TokenType::Identifier)
36038            || self.check(TokenType::QuotedIdentifier)
36039            || self.check(TokenType::Var);
36040
36041        if !has_identifier {
36042            return false;
36043        }
36044
36045        // Look at next token (after the identifier)
36046        let next_pos = self.current + 1;
36047        if next_pos >= self.tokens.len() {
36048            return false;
36049        }
36050
36051        let next_token = &self.tokens[next_pos];
36052
36053        // If next token is comma or rparen, it's simple column aliases
36054        if next_token.token_type == TokenType::Comma || next_token.token_type == TokenType::RParen {
36055            return false;
36056        }
36057
36058        // If next token could be a type name (identifier, var, or type keyword), it's typed columns
36059        // Check for type tokens or identifiers that could be type names
36060        TYPE_TOKENS.contains(&next_token.token_type)
36061            || next_token.token_type == TokenType::Identifier
36062            || next_token.token_type == TokenType::Var
36063    }
36064
36065    /// Check if current token is a no-paren function
36066    fn is_no_paren_function(&self) -> bool {
36067        if self.is_at_end() {
36068            return false;
36069        }
36070        let token_type = self.peek().token_type;
36071        if NO_PAREN_FUNCTIONS.contains(&token_type) {
36072            if !matches!(
36073                self.config.dialect,
36074                Some(crate::dialects::DialectType::ClickHouse)
36075            ) || token_type != TokenType::CurrentTimestamp
36076            {
36077                return true;
36078            }
36079        }
36080        let text_upper = self.peek().text.to_ascii_uppercase();
36081        if crate::function_registry::is_no_paren_function_name_upper(text_upper.as_str()) {
36082            if !matches!(
36083                self.config.dialect,
36084                Some(crate::dialects::DialectType::ClickHouse)
36085            ) || text_upper.as_str() != "CURRENT_TIMESTAMP"
36086            {
36087                return true;
36088            }
36089        }
36090        false
36091    }
36092
36093    /// Match a keyword by text (case-insensitive)
36094    fn match_keyword(&mut self, keyword: &str) -> bool {
36095        if self.is_at_end() {
36096            return false;
36097        }
36098        if self.peek().text.eq_ignore_ascii_case(keyword) {
36099            self.skip();
36100            true
36101        } else {
36102            false
36103        }
36104    }
36105
36106    /// Match a sequence of keywords by text (case-insensitive)
36107    fn match_text_seq(&mut self, keywords: &[&str]) -> bool {
36108        for (i, &kw) in keywords.iter().enumerate() {
36109            if self.current + i >= self.tokens.len() {
36110                return false;
36111            }
36112            if !self.tokens[self.current + i].text.eq_ignore_ascii_case(kw) {
36113                return false;
36114            }
36115        }
36116        self.current += keywords.len();
36117        true
36118    }
36119
36120    /// Check (without consuming) if the next tokens match a sequence of keywords by text (case-insensitive)
36121    fn check_text_seq(&self, keywords: &[&str]) -> bool {
36122        for (i, &kw) in keywords.iter().enumerate() {
36123            if self.current + i >= self.tokens.len() {
36124                return false;
36125            }
36126            if !self.tokens[self.current + i].text.eq_ignore_ascii_case(kw) {
36127                return false;
36128            }
36129        }
36130        true
36131    }
36132
36133    /// Match any of the given texts (case-insensitive)
36134    fn match_texts(&mut self, texts: &[&str]) -> bool {
36135        if self.is_at_end() {
36136            return false;
36137        }
36138        for text in texts {
36139            if self.peek().text.eq_ignore_ascii_case(text) {
36140                self.skip();
36141                return true;
36142            }
36143        }
36144        false
36145    }
36146
36147    /// Parse CASE expression
36148    fn parse_case(&mut self) -> Result<Expression> {
36149        self.expect(TokenType::Case)?;
36150        // Capture trailing comments from the CASE keyword (e.g., CASE /* test */ WHEN ...)
36151        let case_comments = self.previous_trailing_comments().to_vec();
36152
36153        // Check for simple CASE (CASE expr WHEN ...)
36154        let operand = if !self.check(TokenType::When) {
36155            Some(self.parse_expression()?)
36156        } else {
36157            None
36158        };
36159
36160        let mut whens = Vec::new();
36161        while self.match_token(TokenType::When) {
36162            let condition = self.parse_expression()?;
36163            self.expect(TokenType::Then)?;
36164            let mut result = self.parse_expression()?;
36165            // ClickHouse: CASE WHEN x THEN 1 as alias WHEN y THEN alias / 2 END
36166            // Aliases can appear in CASE THEN expressions
36167            if matches!(
36168                self.config.dialect,
36169                Some(crate::dialects::DialectType::ClickHouse)
36170            ) && self.match_token(TokenType::As)
36171            {
36172                let alias = self.expect_identifier_or_keyword()?;
36173                result = Expression::Alias(Box::new(Alias {
36174                    this: result,
36175                    alias: Identifier::new(alias),
36176                    column_aliases: Vec::new(),
36177                    pre_alias_comments: Vec::new(),
36178                    trailing_comments: Vec::new(),
36179                    inferred_type: None,
36180                }));
36181            }
36182            whens.push((condition, result));
36183        }
36184
36185        let else_ = if self.match_token(TokenType::Else) {
36186            Some(self.parse_expression()?)
36187        } else {
36188            None
36189        };
36190
36191        self.expect(TokenType::End)?;
36192
36193        Ok(Expression::Case(Box::new(Case {
36194            operand,
36195            whens,
36196            else_,
36197            comments: case_comments,
36198            inferred_type: None,
36199        })))
36200    }
36201
36202    /// Parse CAST expression
36203    fn parse_cast(&mut self) -> Result<Expression> {
36204        self.expect(TokenType::Cast)?;
36205        self.expect(TokenType::LParen)?;
36206        // Use parse_or() instead of parse_expression() to avoid consuming AS
36207        // as an alias (e.g. CAST((1, 2) AS Tuple(a Int8, b Int16)))
36208        // Python sqlglot uses _parse_disjunction() here, which is equivalent.
36209        let expr = self.parse_or()?;
36210
36211        // ClickHouse: ternary operator inside CAST: CAST(cond ? true_val : false_val AS Type)
36212        let expr = if matches!(
36213            self.config.dialect,
36214            Some(crate::dialects::DialectType::ClickHouse)
36215        ) && self.match_token(TokenType::Parameter)
36216        {
36217            if self.check(TokenType::Colon) {
36218                return Err(
36219                    self.parse_error("Expected true expression after ? in ClickHouse ternary")
36220                );
36221            }
36222            let true_value = self.parse_or()?;
36223            let false_value = if self.match_token(TokenType::Colon) {
36224                self.parse_or()?
36225            } else {
36226                Expression::Null(Null)
36227            };
36228            Expression::IfFunc(Box::new(IfFunc {
36229                original_name: None,
36230                condition: expr,
36231                true_value,
36232                false_value: Some(false_value),
36233                inferred_type: None,
36234            }))
36235        } else {
36236            expr
36237        };
36238
36239        // ClickHouse: implicit alias in CAST: cast('1234' lhs AS UInt32) or cast('1234' lhs, 'UInt32')
36240        let expr = self.try_clickhouse_implicit_alias(expr);
36241
36242        // ClickHouse: CAST(expr, 'type_string') or CAST(expr, expression) syntax with comma instead of AS
36243        if matches!(
36244            self.config.dialect,
36245            Some(crate::dialects::DialectType::ClickHouse)
36246        ) && self.match_token(TokenType::Comma)
36247        {
36248            // Parse as expression to handle concat and other operations: CAST(x, 'Str' || 'ing')
36249            let type_expr = self.parse_expression()?;
36250            // ClickHouse: alias on type expr: cast('1234' lhs, 'UInt32' rhs) or cast('1234', 'UInt32' AS rhs)
36251            let type_expr = self.try_clickhouse_func_arg_alias(type_expr);
36252            self.expect(TokenType::RParen)?;
36253            let _trailing_comments = self.previous_trailing_comments().to_vec();
36254            return Ok(Expression::CastToStrType(Box::new(CastToStrType {
36255                this: Box::new(expr),
36256                to: Some(Box::new(type_expr)),
36257            })));
36258        }
36259
36260        self.expect(TokenType::As)?;
36261
36262        // ClickHouse: CAST(expr AS alias AS Type) — inner alias before type
36263        // If the next token is an identifier followed by AS, treat it as an alias
36264        let expr = if matches!(
36265            self.config.dialect,
36266            Some(crate::dialects::DialectType::ClickHouse)
36267        ) && (self.is_identifier_token() || self.is_safe_keyword_as_identifier())
36268            && self
36269                .peek_nth(1)
36270                .map_or(false, |t| t.token_type == TokenType::As)
36271        {
36272            let alias = self.expect_identifier_or_keyword_with_quoted()?;
36273            self.expect(TokenType::As)?;
36274            Expression::Alias(Box::new(Alias::new(expr, alias)))
36275        } else if matches!(
36276            self.config.dialect,
36277            Some(crate::dialects::DialectType::ClickHouse)
36278        ) && (self.is_identifier_token() || self.is_safe_keyword_as_identifier())
36279            && self
36280                .peek_nth(1)
36281                .map_or(false, |t| t.token_type == TokenType::Comma)
36282        {
36283            // ClickHouse: CAST(expr AS alias, type_string) — alias before comma syntax
36284            let alias = self.expect_identifier_or_keyword_with_quoted()?;
36285            let expr = Expression::Alias(Box::new(Alias::new(expr, alias)));
36286            self.expect(TokenType::Comma)?;
36287            let type_expr = self.parse_expression()?;
36288            let type_expr = self.try_clickhouse_func_arg_alias(type_expr);
36289            self.expect(TokenType::RParen)?;
36290            let _trailing_comments = self.previous_trailing_comments().to_vec();
36291            return Ok(Expression::CastToStrType(Box::new(CastToStrType {
36292                this: Box::new(expr),
36293                to: Some(Box::new(type_expr)),
36294            })));
36295        } else {
36296            expr
36297        };
36298
36299        // Teradata: CAST(x AS FORMAT 'fmt') (no explicit type)
36300        if matches!(
36301            self.config.dialect,
36302            Some(crate::dialects::DialectType::Teradata)
36303        ) && self.match_token(TokenType::Format)
36304        {
36305            let format = Some(Box::new(self.parse_expression()?));
36306            self.expect(TokenType::RParen)?;
36307            let trailing_comments = self.previous_trailing_comments().to_vec();
36308            return Ok(Expression::Cast(Box::new(Cast {
36309                this: expr,
36310                to: DataType::Unknown,
36311                trailing_comments,
36312                double_colon_syntax: false,
36313                format,
36314                default: None,
36315                inferred_type: None,
36316            })));
36317        }
36318
36319        let data_type = self.parse_data_type()?;
36320
36321        // Parse optional DEFAULT ... ON CONVERSION ERROR (Oracle)
36322        // CAST(x AS type DEFAULT val ON CONVERSION ERROR)
36323        let default = if self.match_token(TokenType::Default) {
36324            let default_val = self.parse_primary()?;
36325            // Expect "ON CONVERSION ERROR"
36326            if !self.match_text_seq(&["ON", "CONVERSION", "ERROR"]) {
36327                return Err(self.parse_error("Expected ON CONVERSION ERROR"));
36328            }
36329            Some(Box::new(default_val))
36330        } else {
36331            None
36332        };
36333
36334        // Parse optional FORMAT clause for BigQuery: CAST(x AS STRING FORMAT 'format_string')
36335        // Or for Oracle with comma: CAST(x AS DATE DEFAULT NULL ON CONVERSION ERROR, 'format')
36336        let format = if self.match_token(TokenType::Format) {
36337            Some(Box::new(self.parse_expression()?))
36338        } else if self.match_token(TokenType::Comma) {
36339            // Oracle date format: CAST(x AS DATE, 'format')
36340            Some(Box::new(self.parse_expression()?))
36341        } else {
36342            None
36343        };
36344
36345        self.expect(TokenType::RParen)?;
36346        let trailing_comments = self.previous_trailing_comments().to_vec();
36347
36348        Ok(Expression::Cast(Box::new(Cast {
36349            this: expr,
36350            to: data_type,
36351            trailing_comments,
36352            double_colon_syntax: false,
36353            format,
36354            default,
36355            inferred_type: None,
36356        })))
36357    }
36358
36359    /// Parse TRY_CAST expression
36360    fn parse_try_cast(&mut self) -> Result<Expression> {
36361        self.expect(TokenType::TryCast)?;
36362        self.expect(TokenType::LParen)?;
36363        let expr = self.parse_or()?;
36364        self.expect(TokenType::As)?;
36365        let data_type = self.parse_data_type()?;
36366
36367        // Parse optional FORMAT clause
36368        let format = if self.match_token(TokenType::Format) {
36369            Some(Box::new(self.parse_expression()?))
36370        } else {
36371            None
36372        };
36373
36374        self.expect(TokenType::RParen)?;
36375        let trailing_comments = self.previous_trailing_comments().to_vec();
36376
36377        Ok(Expression::TryCast(Box::new(Cast {
36378            this: expr,
36379            to: data_type,
36380            trailing_comments,
36381            double_colon_syntax: false,
36382            format,
36383            default: None,
36384            inferred_type: None,
36385        })))
36386    }
36387
36388    /// Parse SAFE_CAST expression (BigQuery)
36389    fn parse_safe_cast(&mut self) -> Result<Expression> {
36390        self.expect(TokenType::SafeCast)?;
36391        self.expect(TokenType::LParen)?;
36392        let expr = self.parse_or()?;
36393        self.expect(TokenType::As)?;
36394        let data_type = self.parse_data_type()?;
36395
36396        // Parse optional FORMAT clause
36397        let format = if self.match_token(TokenType::Format) {
36398            Some(Box::new(self.parse_expression()?))
36399        } else {
36400            None
36401        };
36402
36403        self.expect(TokenType::RParen)?;
36404        let trailing_comments = self.previous_trailing_comments().to_vec();
36405
36406        Ok(Expression::SafeCast(Box::new(Cast {
36407            this: expr,
36408            to: data_type,
36409            trailing_comments,
36410            double_colon_syntax: false,
36411            format,
36412            default: None,
36413            inferred_type: None,
36414        })))
36415    }
36416
36417    /// Parse a data type
36418    fn parse_data_type(&mut self) -> Result<DataType> {
36419        // Handle special token types that represent data type keywords
36420        // Teradata tokenizes ST_GEOMETRY as TokenType::Geometry
36421        if self.check(TokenType::Geometry) {
36422            let _token = self.advance();
36423            let (subtype, srid) = self.parse_spatial_type_args()?;
36424            return Ok(DataType::Geometry { subtype, srid });
36425        }
36426        // Data types can be keywords (DATE, TIMESTAMP, etc.) or identifiers
36427        let mut raw_name = self.expect_identifier_or_keyword()?;
36428        // Allow dotted custom types like SYSUDTLIB.INT
36429        while self.match_token(TokenType::Dot) {
36430            let part = self.expect_identifier_or_keyword()?;
36431            raw_name.push('.');
36432            raw_name.push_str(&part);
36433        }
36434        let mut name = raw_name.to_ascii_uppercase();
36435
36436        // SQL standard: NATIONAL CHAR/CHARACTER → NCHAR
36437        if name == "NATIONAL" {
36438            let next_upper = if !self.is_at_end() {
36439                self.peek().text.to_ascii_uppercase()
36440            } else {
36441                String::new()
36442            };
36443            if next_upper == "CHAR" || next_upper == "CHARACTER" {
36444                self.skip(); // consume CHAR/CHARACTER
36445                name = "NCHAR".to_string();
36446                // NATIONAL CHARACTER VARYING → NVARCHAR equivalent
36447                if next_upper == "CHARACTER" && self.check_identifier("VARYING") {
36448                    self.skip(); // consume VARYING
36449                    let length = if self.match_token(TokenType::LParen) {
36450                        if self.check(TokenType::RParen) {
36451                            self.skip();
36452                            None
36453                        } else {
36454                            let n = self.expect_number()? as u32;
36455                            self.expect(TokenType::RParen)?;
36456                            Some(n)
36457                        }
36458                    } else {
36459                        None
36460                    };
36461                    return Ok(DataType::VarChar {
36462                        length,
36463                        parenthesized_length: false,
36464                    });
36465                }
36466            }
36467        }
36468
36469        let base_type = match name.as_str() {
36470            "INT" | "INTEGER" => {
36471                // MySQL allows INT(N) for display width; ClickHouse allows INT()
36472                let length = if self.match_token(TokenType::LParen) {
36473                    if self.check(TokenType::RParen) {
36474                        self.skip();
36475                        None
36476                    } else {
36477                        let n = self.expect_number()? as u32;
36478                        self.expect(TokenType::RParen)?;
36479                        Some(n)
36480                    }
36481                } else {
36482                    None
36483                };
36484                let integer_spelling = name == "INTEGER";
36485                Ok(DataType::Int {
36486                    length,
36487                    integer_spelling,
36488                })
36489            }
36490            "BIGINT" => {
36491                // MySQL allows BIGINT(N) for display width; ClickHouse allows BIGINT()
36492                let length = if self.match_token(TokenType::LParen) {
36493                    if self.check(TokenType::RParen) {
36494                        self.skip();
36495                        None
36496                    } else {
36497                        let n = self.expect_number()? as u32;
36498                        self.expect(TokenType::RParen)?;
36499                        Some(n)
36500                    }
36501                } else {
36502                    None
36503                };
36504                Ok(DataType::BigInt { length })
36505            }
36506            "SMALLINT" => {
36507                let length = if self.match_token(TokenType::LParen) {
36508                    if self.check(TokenType::RParen) {
36509                        self.skip();
36510                        None
36511                    } else {
36512                        let n = self.expect_number()? as u32;
36513                        self.expect(TokenType::RParen)?;
36514                        Some(n)
36515                    }
36516                } else {
36517                    None
36518                };
36519                Ok(DataType::SmallInt { length })
36520            }
36521            "TINYINT" => {
36522                let length = if self.match_token(TokenType::LParen) {
36523                    if self.check(TokenType::RParen) {
36524                        self.skip();
36525                        None
36526                    } else {
36527                        let n = self.expect_number()? as u32;
36528                        self.expect(TokenType::RParen)?;
36529                        Some(n)
36530                    }
36531                } else {
36532                    None
36533                };
36534                Ok(DataType::TinyInt { length })
36535            }
36536            "FLOAT" | "REAL" => {
36537                let real_spelling = name == "REAL";
36538                // MySQL allows FLOAT(precision) or FLOAT(precision, scale)
36539                let (precision, scale) = if self.match_token(TokenType::LParen) {
36540                    let p = self.expect_number()? as u32;
36541                    let s = if self.match_token(TokenType::Comma) {
36542                        Some(self.expect_number()? as u32)
36543                    } else {
36544                        None
36545                    };
36546                    self.expect(TokenType::RParen)?;
36547                    (Some(p), s)
36548                } else {
36549                    (None, None)
36550                };
36551                Ok(DataType::Float {
36552                    precision,
36553                    scale,
36554                    real_spelling,
36555                })
36556            }
36557            "BINARY_FLOAT" => {
36558                // Oracle's BINARY_FLOAT -> DataType::Float
36559                Ok(DataType::Float {
36560                    precision: None,
36561                    scale: None,
36562                    real_spelling: false,
36563                })
36564            }
36565            "BINARY_DOUBLE" => {
36566                // Oracle's BINARY_DOUBLE -> DataType::Double
36567                Ok(DataType::Double {
36568                    precision: None,
36569                    scale: None,
36570                })
36571            }
36572            "DOUBLE" => {
36573                // Handle DOUBLE PRECISION (PostgreSQL standard SQL)
36574                let _ = self.match_identifier("PRECISION");
36575                // MySQL allows DOUBLE(precision, scale)
36576                let (precision, scale) = if self.match_token(TokenType::LParen) {
36577                    let p = self.expect_number()? as u32;
36578                    let s = if self.match_token(TokenType::Comma) {
36579                        Some(self.expect_number()? as u32)
36580                    } else {
36581                        None
36582                    };
36583                    self.expect(TokenType::RParen)?;
36584                    (Some(p), s)
36585                } else {
36586                    (None, None)
36587                };
36588                Ok(DataType::Double { precision, scale })
36589            }
36590            "DECIMAL" | "NUMERIC" => {
36591                let (precision, scale) = if self.match_token(TokenType::LParen) {
36592                    let p = self.expect_number()? as u32;
36593                    let s = if self.match_token(TokenType::Comma) {
36594                        Some(self.expect_number()? as u32)
36595                    } else {
36596                        None
36597                    };
36598                    self.expect(TokenType::RParen)?;
36599                    (Some(p), s)
36600                } else {
36601                    (None, None)
36602                };
36603                Ok(DataType::Decimal { precision, scale })
36604            }
36605            "BOOLEAN" | "BOOL" => Ok(DataType::Boolean),
36606            "CHAR" | "CHARACTER" | "NCHAR" => {
36607                let is_nchar = name == "NCHAR";
36608                // SQL standard: CHARACTER LARGE OBJECT → CLOB/TEXT
36609                if self.match_identifier("LARGE") && self.match_identifier("OBJECT") {
36610                    return Ok(DataType::Text);
36611                }
36612                // Check for VARYING to convert to VARCHAR (SQL standard: CHAR VARYING, CHARACTER VARYING)
36613                if self.match_identifier("VARYING") {
36614                    let length = if self.match_token(TokenType::LParen) {
36615                        if self.check(TokenType::RParen) {
36616                            self.skip();
36617                            None
36618                        } else {
36619                            let n = self.expect_number()? as u32;
36620                            self.expect(TokenType::RParen)?;
36621                            Some(n)
36622                        }
36623                    } else {
36624                        None
36625                    };
36626                    Ok(DataType::VarChar {
36627                        length,
36628                        parenthesized_length: false,
36629                    })
36630                } else {
36631                    let length = if self.match_token(TokenType::LParen) {
36632                        // Allow empty parens like NCHAR() - treat as no length specified
36633                        if self.check(TokenType::RParen) {
36634                            self.skip(); // consume RParen
36635                            None
36636                        } else {
36637                            let n = self.expect_number()? as u32;
36638                            self.expect(TokenType::RParen)?;
36639                            Some(n)
36640                        }
36641                    } else {
36642                        None
36643                    };
36644                    // CHAR CHARACTER SET charset (MySQL CAST context, no length)
36645                    // When length is specified (e.g., CHAR(4) CHARACTER SET LATIN),
36646                    // CHARACTER SET is a column attribute handled at the column def level
36647                    if length.is_none()
36648                        && self.match_identifier("CHARACTER")
36649                        && self.match_token(TokenType::Set)
36650                    {
36651                        let charset = self.expect_identifier_or_keyword()?;
36652                        return Ok(DataType::CharacterSet { name: charset });
36653                    }
36654                    // Preserve NCHAR as Custom DataType so target dialects can map it properly
36655                    // (Oracle keeps NCHAR, TSQL keeps NCHAR, others map to CHAR)
36656                    if is_nchar {
36657                        let name = if let Some(len) = length {
36658                            format!("NCHAR({})", len)
36659                        } else {
36660                            "NCHAR".to_string()
36661                        };
36662                        return Ok(DataType::Custom { name });
36663                    }
36664                    Ok(DataType::Char { length })
36665                }
36666            }
36667            "VARCHAR" | "NVARCHAR" => {
36668                let is_nvarchar = name == "NVARCHAR";
36669                if self.match_token(TokenType::LParen) {
36670                    // Allow empty parens like NVARCHAR() - treat as no length specified
36671                    if self.check(TokenType::RParen) {
36672                        self.skip(); // consume RParen
36673                        if is_nvarchar {
36674                            return Ok(DataType::Custom {
36675                                name: "NVARCHAR".to_string(),
36676                            });
36677                        }
36678                        Ok(DataType::VarChar {
36679                            length: None,
36680                            parenthesized_length: false,
36681                        })
36682                    } else if self.check_identifier("MAX") {
36683                        // TSQL: VARCHAR(MAX) / NVARCHAR(MAX)
36684                        self.skip(); // consume MAX
36685                        self.expect(TokenType::RParen)?;
36686                        let type_name = if is_nvarchar {
36687                            "NVARCHAR(MAX)"
36688                        } else {
36689                            "VARCHAR(MAX)"
36690                        };
36691                        Ok(DataType::Custom {
36692                            name: type_name.to_string(),
36693                        })
36694                    } else {
36695                        // Hive allows VARCHAR((50)) - extra parentheses around the length
36696                        let parenthesized_length = self.match_token(TokenType::LParen);
36697                        let n = self.expect_number()? as u32;
36698                        if parenthesized_length {
36699                            self.expect(TokenType::RParen)?;
36700                        }
36701                        self.expect(TokenType::RParen)?;
36702                        // Preserve NVARCHAR as Custom DataType so target dialects can map properly
36703                        if is_nvarchar {
36704                            return Ok(DataType::Custom {
36705                                name: format!("NVARCHAR({})", n),
36706                            });
36707                        }
36708                        Ok(DataType::VarChar {
36709                            length: Some(n),
36710                            parenthesized_length,
36711                        })
36712                    }
36713                } else {
36714                    if is_nvarchar {
36715                        return Ok(DataType::Custom {
36716                            name: "NVARCHAR".to_string(),
36717                        });
36718                    }
36719                    Ok(DataType::VarChar {
36720                        length: None,
36721                        parenthesized_length: false,
36722                    })
36723                }
36724            }
36725            "TEXT" | "NTEXT" => {
36726                // TEXT(n) - optional length parameter
36727                if self.match_token(TokenType::LParen) {
36728                    let n = self.expect_number()? as u32;
36729                    self.expect(TokenType::RParen)?;
36730                    Ok(DataType::TextWithLength { length: n })
36731                } else {
36732                    Ok(DataType::Text)
36733                }
36734            }
36735            "STRING" => {
36736                // BigQuery STRING(n) - parameterized string with max length
36737                let length = if self.match_token(TokenType::LParen) {
36738                    let n = self.expect_number()? as u32;
36739                    self.expect(TokenType::RParen)?;
36740                    Some(n)
36741                } else {
36742                    None
36743                };
36744                Ok(DataType::String { length })
36745            }
36746            "DATE" => Ok(DataType::Date),
36747            "TIME" => {
36748                // ClickHouse: Time('timezone') is a custom type with string arg
36749                if matches!(
36750                    self.config.dialect,
36751                    Some(crate::dialects::DialectType::ClickHouse)
36752                ) && self.check(TokenType::LParen)
36753                    && self.current + 1 < self.tokens.len()
36754                    && self.tokens[self.current + 1].token_type == TokenType::String
36755                {
36756                    self.skip(); // consume LParen
36757                    let args = self.parse_custom_type_args_balanced()?;
36758                    self.expect(TokenType::RParen)?;
36759                    return Ok(DataType::Custom {
36760                        name: format!("Time({})", args),
36761                    });
36762                }
36763                let precision = if self.match_token(TokenType::LParen) {
36764                    if self.check(TokenType::RParen) {
36765                        self.skip();
36766                        None
36767                    } else {
36768                        let p = self.expect_number()? as u32;
36769                        self.expect(TokenType::RParen)?;
36770                        Some(p)
36771                    }
36772                } else {
36773                    None
36774                };
36775                // Handle TIME WITH/WITHOUT TIME ZONE
36776                let timezone = if self.match_token(TokenType::With) {
36777                    self.match_keyword("TIME");
36778                    self.match_keyword("ZONE");
36779                    true
36780                } else if self.match_keyword("WITHOUT") {
36781                    self.match_keyword("TIME");
36782                    self.match_keyword("ZONE");
36783                    false
36784                } else {
36785                    false
36786                };
36787                Ok(DataType::Time {
36788                    precision,
36789                    timezone,
36790                })
36791            }
36792            "TIMETZ" => {
36793                let precision = if self.match_token(TokenType::LParen) {
36794                    let p = self.expect_number()? as u32;
36795                    self.expect(TokenType::RParen)?;
36796                    Some(p)
36797                } else {
36798                    None
36799                };
36800                Ok(DataType::Time {
36801                    precision,
36802                    timezone: true,
36803                })
36804            }
36805            "TIMESTAMP" => {
36806                // Parse optional precision: TIMESTAMP(p)
36807                let precision = if self.match_token(TokenType::LParen) {
36808                    let p = self.expect_number()? as u32;
36809                    self.expect(TokenType::RParen)?;
36810                    Some(p)
36811                } else {
36812                    None
36813                };
36814                // Parse optional WITH/WITHOUT TIME ZONE or WITH LOCAL TIME ZONE
36815                // Note: TIME is a keyword (TokenType::Time) and LOCAL is a keyword (TokenType::Local)
36816                if self.match_token(TokenType::With) {
36817                    // Check for LOCAL TIME ZONE (Exasol) vs TIME ZONE
36818                    // LOCAL is tokenized as TokenType::Local, not as Identifier
36819                    if self.match_token(TokenType::Local) {
36820                        self.match_keyword("TIME");
36821                        self.match_keyword("ZONE");
36822                        // TIMESTAMP WITH LOCAL TIME ZONE - return as custom type for Exasol handling
36823                        Ok(DataType::Custom {
36824                            name: "TIMESTAMPLTZ".to_string(),
36825                        })
36826                    } else {
36827                        self.match_keyword("TIME");
36828                        self.match_keyword("ZONE");
36829                        Ok(DataType::Timestamp {
36830                            precision,
36831                            timezone: true,
36832                        })
36833                    }
36834                } else if self.match_keyword("WITHOUT") {
36835                    self.match_keyword("TIME");
36836                    self.match_keyword("ZONE");
36837                    Ok(DataType::Timestamp {
36838                        precision,
36839                        timezone: false,
36840                    })
36841                } else {
36842                    Ok(DataType::Timestamp {
36843                        precision,
36844                        timezone: false,
36845                    })
36846                }
36847            }
36848            "TIMESTAMPTZ" => {
36849                let precision = if self.match_token(TokenType::LParen) {
36850                    let p = self.expect_number()? as u32;
36851                    self.expect(TokenType::RParen)?;
36852                    Some(p)
36853                } else {
36854                    None
36855                };
36856                Ok(DataType::Timestamp {
36857                    precision,
36858                    timezone: true,
36859                })
36860            }
36861            "TIMESTAMPLTZ" | "TIMESTAMP_LTZ" => {
36862                let precision = if self.match_token(TokenType::LParen) {
36863                    let p = self.expect_number()? as u32;
36864                    self.expect(TokenType::RParen)?;
36865                    Some(p)
36866                } else {
36867                    None
36868                };
36869                let name = if let Some(p) = precision {
36870                    format!("TIMESTAMPLTZ({})", p)
36871                } else {
36872                    "TIMESTAMPLTZ".to_string()
36873                };
36874                Ok(DataType::Custom { name })
36875            }
36876            "INTERVAL" => {
36877                // Parse optional unit (DAYS, DAY, HOUR, etc.)
36878                // Don't consume GENERATED, AS, NOT, NULL, etc. which are column constraints
36879                let unit = if (self.check(TokenType::Identifier)
36880                    || self.check(TokenType::Var)
36881                    || self.check_keyword())
36882                    && !self.check(TokenType::Generated)
36883                    && !self.check(TokenType::As)
36884                    && !self.check(TokenType::Not)
36885                    && !self.check(TokenType::Null)
36886                    && !self.check(TokenType::Default)
36887                    && !self.check(TokenType::PrimaryKey)
36888                    && !self.check(TokenType::Unique)
36889                    && !self.check(TokenType::Check)
36890                    && !self.check(TokenType::Constraint)
36891                    && !self.check(TokenType::References)
36892                    && !self.check(TokenType::Collate)
36893                    && !self.check(TokenType::Comment)
36894                    && !self.check(TokenType::RParen)
36895                    && !self.check(TokenType::Comma)
36896                {
36897                    Some(self.advance().text.to_ascii_uppercase())
36898                } else {
36899                    None
36900                };
36901                // Parse optional TO unit for range intervals like DAY TO HOUR
36902                let to = if self.match_token(TokenType::To) {
36903                    if self.check(TokenType::Identifier)
36904                        || self.check(TokenType::Var)
36905                        || self.check_keyword()
36906                    {
36907                        Some(self.advance().text.to_ascii_uppercase())
36908                    } else {
36909                        None
36910                    }
36911                } else {
36912                    None
36913                };
36914                Ok(DataType::Interval { unit, to })
36915            }
36916            "JSON" => {
36917                if matches!(
36918                    self.config.dialect,
36919                    Some(crate::dialects::DialectType::ClickHouse)
36920                ) && self.match_token(TokenType::LParen)
36921                {
36922                    // ClickHouse: JSON(subcolumn_specs) e.g. JSON(a String, b UInt32) or JSON(max_dynamic_paths=8)
36923                    let args = self.parse_custom_type_args_balanced()?;
36924                    self.expect(TokenType::RParen)?;
36925                    Ok(DataType::Custom {
36926                        name: format!("JSON({})", args),
36927                    })
36928                } else {
36929                    Ok(DataType::Json)
36930                }
36931            }
36932            "JSONB" => Ok(DataType::JsonB),
36933            "UUID" => Ok(DataType::Uuid),
36934            "BLOB" => Ok(DataType::Blob),
36935            "BYTEA" => Ok(DataType::VarBinary { length: None }),
36936            "BIT" => {
36937                let length = if self.match_token(TokenType::LParen) {
36938                    let n = self.expect_number()? as u32;
36939                    self.expect(TokenType::RParen)?;
36940                    Some(n)
36941                } else {
36942                    None
36943                };
36944                Ok(DataType::Bit { length })
36945            }
36946            "VARBIT" | "BIT VARYING" => {
36947                let length = if self.match_token(TokenType::LParen) {
36948                    let n = self.expect_number()? as u32;
36949                    self.expect(TokenType::RParen)?;
36950                    Some(n)
36951                } else {
36952                    None
36953                };
36954                Ok(DataType::VarBit { length })
36955            }
36956            "BINARY" => {
36957                // SQL standard: BINARY LARGE OBJECT → BLOB
36958                if self.match_identifier("LARGE") && self.match_identifier("OBJECT") {
36959                    return Ok(DataType::Blob);
36960                }
36961                // Handle BINARY VARYING (SQL standard for VARBINARY)
36962                if self.match_identifier("VARYING") {
36963                    let length = if self.match_token(TokenType::LParen) {
36964                        let len = self.expect_number()? as u32;
36965                        self.expect(TokenType::RParen)?;
36966                        Some(len)
36967                    } else {
36968                        None
36969                    };
36970                    Ok(DataType::VarBinary { length })
36971                } else {
36972                    let length = if self.match_token(TokenType::LParen) {
36973                        let len = self.expect_number()? as u32;
36974                        self.expect(TokenType::RParen)?;
36975                        Some(len)
36976                    } else {
36977                        None
36978                    };
36979                    Ok(DataType::Binary { length })
36980                }
36981            }
36982            "VARBINARY" => {
36983                let length = if self.match_token(TokenType::LParen) {
36984                    let len = self.expect_number()? as u32;
36985                    self.expect(TokenType::RParen)?;
36986                    Some(len)
36987                } else {
36988                    None
36989                };
36990                Ok(DataType::VarBinary { length })
36991            }
36992            // Generic types with angle bracket or parentheses syntax: ARRAY<T>, ARRAY(T), MAP<K,V>, MAP(K,V)
36993            "ARRAY" => {
36994                if self.match_token(TokenType::Lt) {
36995                    // ARRAY<element_type> - angle bracket style
36996                    let element_type = self.parse_data_type()?;
36997                    self.expect_gt()?;
36998                    Ok(DataType::Array {
36999                        element_type: Box::new(element_type),
37000                        dimension: None,
37001                    })
37002                } else if self.match_token(TokenType::LParen) {
37003                    // ARRAY(element_type) - Snowflake parentheses style
37004                    let element_type = self.parse_data_type()?;
37005                    self.expect(TokenType::RParen)?;
37006                    Ok(DataType::Array {
37007                        element_type: Box::new(element_type),
37008                        dimension: None,
37009                    })
37010                } else {
37011                    // Just ARRAY without type parameter
37012                    Ok(DataType::Custom {
37013                        name: "ARRAY".to_string(),
37014                    })
37015                }
37016            }
37017            "MAP" => {
37018                if self.match_token(TokenType::Lt) {
37019                    // MAP<key_type, value_type> - angle bracket style
37020                    let key_type = self.parse_data_type()?;
37021                    self.expect(TokenType::Comma)?;
37022                    let value_type = self.parse_data_type()?;
37023                    self.expect_gt()?;
37024                    Ok(DataType::Map {
37025                        key_type: Box::new(key_type),
37026                        value_type: Box::new(value_type),
37027                    })
37028                } else if self.match_token(TokenType::LBracket) {
37029                    // Materialize: MAP[TEXT => INT] type syntax
37030                    let key_type = self.parse_data_type()?;
37031                    self.expect(TokenType::FArrow)?;
37032                    let value_type = self.parse_data_type()?;
37033                    self.expect(TokenType::RBracket)?;
37034                    Ok(DataType::Map {
37035                        key_type: Box::new(key_type),
37036                        value_type: Box::new(value_type),
37037                    })
37038                } else if self.match_token(TokenType::LParen) {
37039                    // MAP(key_type, value_type) - Snowflake parentheses style
37040                    let key_type = self.parse_data_type()?;
37041                    self.expect(TokenType::Comma)?;
37042                    let value_type = self.parse_data_type()?;
37043                    self.expect(TokenType::RParen)?;
37044                    Ok(DataType::Map {
37045                        key_type: Box::new(key_type),
37046                        value_type: Box::new(value_type),
37047                    })
37048                } else {
37049                    // Just MAP without type parameters
37050                    Ok(DataType::Custom {
37051                        name: "MAP".to_string(),
37052                    })
37053                }
37054            }
37055            // VECTOR(type, dimension) - Snowflake vector type
37056            // VECTOR(dimension, element_type_alias) or VECTOR(dimension) - SingleStore vector type
37057            "VECTOR" => {
37058                if self.match_token(TokenType::LParen) {
37059                    if self.check(TokenType::Number) {
37060                        // SingleStore format: VECTOR(dimension) or VECTOR(dimension, type_alias)
37061                        let dimension = self.expect_number()? as u32;
37062                        let element_type = if self.match_token(TokenType::Comma) {
37063                            // Parse the type alias (I8, I16, I32, I64, F32, F64)
37064                            let type_alias = self.expect_identifier_or_keyword()?;
37065                            let mapped_type = match type_alias.to_ascii_uppercase().as_str() {
37066                                "I8" => DataType::TinyInt { length: None },
37067                                "I16" => DataType::SmallInt { length: None },
37068                                "I32" => DataType::Int {
37069                                    length: None,
37070                                    integer_spelling: false,
37071                                },
37072                                "I64" => DataType::BigInt { length: None },
37073                                "F32" => DataType::Float {
37074                                    precision: None,
37075                                    scale: None,
37076                                    real_spelling: false,
37077                                },
37078                                "F64" => DataType::Double {
37079                                    precision: None,
37080                                    scale: None,
37081                                },
37082                                _ => DataType::Custom {
37083                                    name: type_alias.to_string(),
37084                                },
37085                            };
37086                            Some(Box::new(mapped_type))
37087                        } else {
37088                            // Just dimension, no type
37089                            None
37090                        };
37091                        self.expect(TokenType::RParen)?;
37092                        Ok(DataType::Vector {
37093                            element_type,
37094                            dimension: Some(dimension),
37095                        })
37096                    } else {
37097                        // Snowflake format: VECTOR(type, dimension)
37098                        let element_type = self.parse_data_type()?;
37099                        self.expect(TokenType::Comma)?;
37100                        let dimension = self.expect_number()? as u32;
37101                        self.expect(TokenType::RParen)?;
37102                        Ok(DataType::Vector {
37103                            element_type: Some(Box::new(element_type)),
37104                            dimension: Some(dimension),
37105                        })
37106                    }
37107                } else {
37108                    Ok(DataType::Custom {
37109                        name: "VECTOR".to_string(),
37110                    })
37111                }
37112            }
37113            // OBJECT(field1 type1, field2 type2, ...) - Snowflake structured object type
37114            "OBJECT" => {
37115                if self.match_token(TokenType::LParen) {
37116                    // ClickHouse: Object('json') — string literal argument
37117                    if matches!(
37118                        self.config.dialect,
37119                        Some(crate::dialects::DialectType::ClickHouse)
37120                    ) && self.check(TokenType::String)
37121                    {
37122                        let arg = self.advance().text;
37123                        self.expect(TokenType::RParen)?;
37124                        return Ok(DataType::Custom {
37125                            name: format!("Object('{}')", arg),
37126                        });
37127                    }
37128                    let mut fields = Vec::new();
37129                    if !self.check(TokenType::RParen) {
37130                        loop {
37131                            let field_name = self.expect_identifier_or_keyword()?;
37132                            let field_type = self.parse_data_type()?;
37133                            // Optional NOT NULL constraint
37134                            let not_null = if self.match_keyword("NOT") {
37135                                // Consume NULL if present
37136                                self.match_keyword("NULL");
37137                                true
37138                            } else {
37139                                false
37140                            };
37141                            fields.push((field_name, field_type, not_null));
37142                            if !self.match_token(TokenType::Comma) {
37143                                break;
37144                            }
37145                        }
37146                    }
37147                    self.expect(TokenType::RParen)?;
37148                    // Check for RENAME FIELDS or ADD FIELDS modifier
37149                    let modifier = if self.match_keyword("RENAME") {
37150                        if self.match_keyword("FIELDS") {
37151                            Some("RENAME FIELDS".to_string())
37152                        } else {
37153                            Some("RENAME".to_string())
37154                        }
37155                    } else if self.match_keyword("ADD") {
37156                        if self.match_keyword("FIELDS") {
37157                            Some("ADD FIELDS".to_string())
37158                        } else {
37159                            Some("ADD".to_string())
37160                        }
37161                    } else {
37162                        None
37163                    };
37164                    Ok(DataType::Object { fields, modifier })
37165                } else {
37166                    Ok(DataType::Custom {
37167                        name: "OBJECT".to_string(),
37168                    })
37169                }
37170            }
37171            "STRUCT" => {
37172                if self.match_token(TokenType::Lt) {
37173                    // STRUCT<field1 type1, field2 type2, ...> - BigQuery angle-bracket syntax
37174                    let fields = self.parse_struct_type_fields(false)?;
37175                    self.expect_gt()?;
37176                    Ok(DataType::Struct {
37177                        fields,
37178                        nested: false,
37179                    })
37180                } else if self.match_token(TokenType::LParen) {
37181                    // STRUCT(field1 type1, field2 type2, ...) - DuckDB parenthesized syntax
37182                    let fields = self.parse_struct_type_fields(true)?;
37183                    self.expect(TokenType::RParen)?;
37184                    Ok(DataType::Struct {
37185                        fields,
37186                        nested: true,
37187                    })
37188                } else {
37189                    // Just STRUCT without type parameters
37190                    Ok(DataType::Custom {
37191                        name: "STRUCT".to_string(),
37192                    })
37193                }
37194            }
37195            "ROW" => {
37196                // ROW(field1 type1, field2 type2, ...) - same as STRUCT with parens
37197                if self.match_token(TokenType::LParen) {
37198                    let fields = self.parse_struct_type_fields(true)?;
37199                    self.expect(TokenType::RParen)?;
37200                    Ok(DataType::Struct {
37201                        fields,
37202                        nested: true,
37203                    })
37204                } else {
37205                    Ok(DataType::Custom {
37206                        name: "ROW".to_string(),
37207                    })
37208                }
37209            }
37210            "RECORD" => {
37211                // RECORD(field1 type1, field2 type2, ...) - SingleStore record type (like ROW/STRUCT)
37212                if self.match_token(TokenType::LParen) {
37213                    let fields = self.parse_struct_type_fields(true)?;
37214                    self.expect(TokenType::RParen)?;
37215                    // Use Struct with nested=true, generator will output RECORD for SingleStore
37216                    Ok(DataType::Struct {
37217                        fields,
37218                        nested: true,
37219                    })
37220                } else {
37221                    Ok(DataType::Custom {
37222                        name: "RECORD".to_string(),
37223                    })
37224                }
37225            }
37226            "ENUM" => {
37227                // ENUM('RED', 'GREEN', 'BLUE') - DuckDB enum type
37228                // ClickHouse: Enum('hello' = 1, 'world' = 2)
37229                // ClickHouse also allows NULL in enum: Enum('a', 'b', NULL)
37230                if self.match_token(TokenType::LParen) {
37231                    let mut values = Vec::new();
37232                    let mut assignments = Vec::new();
37233                    if !self.check(TokenType::RParen) {
37234                        loop {
37235                            let val = if matches!(
37236                                self.config.dialect,
37237                                Some(crate::dialects::DialectType::ClickHouse)
37238                            ) && self.check(TokenType::Null)
37239                            {
37240                                self.skip();
37241                                "NULL".to_string()
37242                            } else {
37243                                self.expect_string()?
37244                            };
37245                            values.push(val);
37246                            // ClickHouse: optional = value assignment (including negative numbers)
37247                            if self.match_token(TokenType::Eq) {
37248                                let negative = self.match_token(TokenType::Dash);
37249                                let num_token = self.advance();
37250                                let val = if negative {
37251                                    format!("-{}", num_token.text)
37252                                } else {
37253                                    num_token.text.clone()
37254                                };
37255                                assignments.push(Some(val));
37256                            } else {
37257                                assignments.push(None);
37258                            }
37259                            if !self.match_token(TokenType::Comma) {
37260                                break;
37261                            }
37262                        }
37263                    }
37264                    self.expect(TokenType::RParen)?;
37265                    Ok(DataType::Enum {
37266                        values,
37267                        assignments,
37268                    })
37269                } else {
37270                    Ok(DataType::Custom {
37271                        name: "ENUM".to_string(),
37272                    })
37273                }
37274            }
37275            "SET" => {
37276                // MySQL SET('a', 'b', 'c') type
37277                if self.match_token(TokenType::LParen) {
37278                    let mut values = Vec::new();
37279                    if !self.check(TokenType::RParen) {
37280                        loop {
37281                            let val = self.expect_string()?;
37282                            values.push(val);
37283                            if !self.match_token(TokenType::Comma) {
37284                                break;
37285                            }
37286                        }
37287                    }
37288                    self.expect(TokenType::RParen)?;
37289                    Ok(DataType::Set { values })
37290                } else {
37291                    Ok(DataType::Custom {
37292                        name: "SET".to_string(),
37293                    })
37294                }
37295            }
37296            "UNION" if self.check(TokenType::LParen) => {
37297                // UNION(num INT, str TEXT) - DuckDB union type (only when followed by paren)
37298                self.skip(); // consume LParen
37299                let struct_fields = self.parse_struct_type_fields(true)?;
37300                self.expect(TokenType::RParen)?;
37301                // Convert StructField to (String, DataType) for Union
37302                let fields: Vec<(String, DataType)> = struct_fields
37303                    .into_iter()
37304                    .map(|f| (f.name, f.data_type))
37305                    .collect();
37306                Ok(DataType::Union { fields })
37307            }
37308            // Spatial types
37309            "GEOMETRY" => {
37310                let (subtype, srid) = self.parse_spatial_type_args()?;
37311                Ok(DataType::Geometry { subtype, srid })
37312            }
37313            "GEOGRAPHY" => {
37314                let (subtype, srid) = self.parse_spatial_type_args()?;
37315                Ok(DataType::Geography { subtype, srid })
37316            }
37317            // MySQL spatial subtypes without wrapper
37318            "POINT" | "LINESTRING" | "POLYGON" | "MULTIPOINT" | "MULTILINESTRING"
37319            | "MULTIPOLYGON" | "GEOMETRYCOLLECTION" => {
37320                // Check for optional SRID clause (MySQL syntax)
37321                let srid = if self.match_identifier("SRID") {
37322                    Some(self.expect_number()? as u32)
37323                } else {
37324                    None
37325                };
37326                Ok(DataType::Geometry {
37327                    subtype: Some(name),
37328                    srid,
37329                })
37330            }
37331            // BigQuery ANY TYPE - templated parameter type for UDFs
37332            "ANY" => {
37333                if self.match_token(TokenType::Type) {
37334                    Ok(DataType::Custom {
37335                        name: "ANY TYPE".to_string(),
37336                    })
37337                } else {
37338                    Ok(DataType::Custom {
37339                        name: "ANY".to_string(),
37340                    })
37341                }
37342            }
37343            // LONG VARCHAR (Exasol) - same as TEXT
37344            "LONG" => {
37345                if self.match_identifier("VARCHAR") {
37346                    Ok(DataType::Text)
37347                } else {
37348                    Ok(DataType::Custom {
37349                        name: "LONG".to_string(),
37350                    })
37351                }
37352            }
37353            // MySQL SIGNED [INTEGER] / UNSIGNED [INTEGER] in CAST context
37354            // CAST(x AS SIGNED INTEGER) -> CAST(x AS SIGNED)
37355            "SIGNED" | "UNSIGNED" => {
37356                // Consume optional INTEGER keyword after SIGNED/UNSIGNED
37357                if self.check_identifier("INTEGER")
37358                    || self.check_keyword_text("INTEGER")
37359                    || self.check_keyword_text("INT")
37360                {
37361                    self.skip();
37362                }
37363                Ok(DataType::Custom { name })
37364            }
37365            // ClickHouse Nullable(T) wrapper type
37366            "NULLABLE" => {
37367                self.expect(TokenType::LParen)?;
37368                let inner = self.parse_data_type()?;
37369                self.expect(TokenType::RParen)?;
37370                Ok(DataType::Nullable {
37371                    inner: Box::new(inner),
37372                })
37373            }
37374            _ => {
37375                // Handle custom types with optional parenthesized precision/args
37376                // e.g., DATETIME2(2), DATETIMEOFFSET(7), NVARCHAR2(100)
37377                // Use uppercase name for known SQL custom types, but preserve original case
37378                // for user-defined type names (e.g., UserDefinedTableType)
37379                let is_known = convert_name_is_known_custom(&name);
37380                let custom_name = if is_known {
37381                    name.clone()
37382                } else {
37383                    raw_name.clone()
37384                };
37385                if self.match_token(TokenType::LParen) {
37386                    if matches!(
37387                        self.config.dialect,
37388                        Some(crate::dialects::DialectType::ClickHouse)
37389                    ) {
37390                        let args = self.parse_custom_type_args_balanced()?;
37391                        self.expect(TokenType::RParen)?;
37392                        Ok(DataType::Custom {
37393                            name: format!("{}({})", custom_name, args),
37394                        })
37395                    } else {
37396                        let mut args = Vec::new();
37397                        let mut after_comma = true; // treat first token as start of new arg
37398                        loop {
37399                            if self.check(TokenType::RParen) {
37400                                break;
37401                            }
37402                            let token = self.advance();
37403                            // If the previous token was space-separated (not comma-separated),
37404                            // append to the last arg. E.g., VARCHAR2(2328 CHAR) -> "2328 CHAR"
37405                            if !after_comma && !args.is_empty() {
37406                                if let Some(last) = args.last_mut() {
37407                                    *last = format!("{} {}", last, token.text);
37408                                }
37409                            } else {
37410                                args.push(token.text.clone());
37411                            }
37412                            after_comma = self.match_token(TokenType::Comma);
37413                        }
37414                        self.expect(TokenType::RParen)?;
37415                        // Include args in the name: DATETIME2(2), VARCHAR2(2328 CHAR)
37416                        Ok(DataType::Custom {
37417                            name: format!("{}({})", custom_name, args.join(", ")),
37418                        })
37419                    }
37420                } else {
37421                    Ok(DataType::Custom { name: custom_name })
37422                }
37423            }
37424        }?;
37425
37426        // UNSIGNED/SIGNED modifiers for integer types (MySQL) are handled
37427        // by the column definition parser which sets col.unsigned = true.
37428        // Do NOT consume them here; the column parser needs to see them.
37429        let mut result_type = base_type;
37430
37431        // Materialize: handle postfix LIST syntax (INT LIST, INT LIST LIST LIST)
37432        let is_materialize = matches!(
37433            self.config.dialect,
37434            Some(crate::dialects::DialectType::Materialize)
37435        );
37436        if is_materialize {
37437            while self.check_identifier("LIST") || self.check(TokenType::List) {
37438                self.skip(); // consume LIST
37439                result_type = DataType::List {
37440                    element_type: Box::new(result_type),
37441                };
37442            }
37443        }
37444
37445        // PostgreSQL array syntax: TYPE[], TYPE[N], TYPE[N][M], etc.
37446        let result_type = self.maybe_parse_array_dimensions(result_type)?;
37447
37448        // ClickHouse: mark string-like standard types as non-nullable by converting to Custom
37449        // This prevents the generator from wrapping them in Nullable() during identity transforms.
37450        // Types parsed from other dialects remain standard and will get Nullable wrapping when
37451        // transpiling to ClickHouse.
37452        if matches!(self.config.dialect, Some(crate::dialects::DialectType::ClickHouse)) {
37453            return Ok(Self::clickhouse_mark_non_nullable(result_type));
37454        }
37455
37456        Ok(result_type)
37457    }
37458
37459    /// Convert standard types to Custom equivalents for ClickHouse to prevent Nullable wrapping.
37460    /// This mirrors Python sqlglot's behavior of marking ClickHouse-parsed types as non-nullable.
37461    fn clickhouse_mark_non_nullable(dt: DataType) -> DataType {
37462        match dt {
37463            DataType::Text => DataType::Custom { name: "String".to_string() },
37464            DataType::VarChar { .. } => DataType::Custom { name: "String".to_string() },
37465            DataType::Char { .. } => DataType::Custom { name: "String".to_string() },
37466            DataType::String { .. } => DataType::Custom { name: "String".to_string() },
37467            _ => dt,
37468        }
37469    }
37470
37471    /// Parse a data type for cast syntax (::TYPE)
37472    /// For dialects that support fixed-size arrays (like DuckDB), brackets like [3] are
37473    /// parsed as array dimensions (e.g., x::INT[3] means cast to INT[3] array type).
37474    /// For other dialects (like Snowflake), brackets are subscript operations
37475    /// (e.g., x::VARIANT[0] means cast to VARIANT, then subscript with [0]).
37476    fn parse_data_type_for_cast(&mut self) -> Result<DataType> {
37477        // Check if dialect supports array type suffixes (e.g., INT[], VARCHAR[3])
37478        // PostgreSQL: INT[], TEXT[] (no fixed size)
37479        // DuckDB: INT[3] (fixed size arrays)
37480        let supports_array_type_suffix = matches!(
37481            self.config.dialect,
37482            Some(crate::dialects::DialectType::DuckDB)
37483                | Some(crate::dialects::DialectType::PostgreSQL)
37484                | Some(crate::dialects::DialectType::Redshift)
37485        );
37486
37487        // Check if it's a quoted identifier (e.g., "udt") — preserve case and quoting
37488        let is_quoted = self.check(TokenType::QuotedIdentifier);
37489        let raw_name = self.expect_identifier_or_keyword()?;
37490        if is_quoted {
37491            // Check if the quoted name matches a known type — if so, normalize it
37492            let known_type = self.convert_name_to_type(&raw_name);
37493            if let Ok(ref dt) = known_type {
37494                if !matches!(dt, DataType::Custom { .. }) {
37495                    return known_type;
37496                }
37497            }
37498            // Truly custom type — preserve original case with quotes
37499            return Ok(DataType::Custom {
37500                name: format!("\"{}\"", raw_name),
37501            });
37502        }
37503        let name = raw_name.to_ascii_uppercase();
37504
37505        // Handle parametric types like ARRAY<T>, MAP<K,V>
37506        let base_type = match name.as_str() {
37507            "ARRAY" => {
37508                if self.match_token(TokenType::Lt) {
37509                    let element_type = self.parse_data_type()?;
37510                    self.expect_gt()?;
37511                    DataType::Array {
37512                        element_type: Box::new(element_type),
37513                        dimension: None,
37514                    }
37515                } else if self.match_token(TokenType::LParen) {
37516                    // ClickHouse: Array(Type) syntax with parentheses
37517                    let element_type = self.parse_data_type_for_cast()?;
37518                    self.expect(TokenType::RParen)?;
37519                    DataType::Array {
37520                        element_type: Box::new(element_type),
37521                        dimension: None,
37522                    }
37523                } else {
37524                    DataType::Custom { name }
37525                }
37526            }
37527            "MAP" => {
37528                if self.match_token(TokenType::Lt) {
37529                    let key_type = self.parse_data_type()?;
37530                    self.expect(TokenType::Comma)?;
37531                    let value_type = self.parse_data_type()?;
37532                    self.expect_gt()?;
37533                    DataType::Map {
37534                        key_type: Box::new(key_type),
37535                        value_type: Box::new(value_type),
37536                    }
37537                } else if self.match_token(TokenType::LParen) {
37538                    // Snowflake: MAP(key_type, value_type) syntax
37539                    let key_type = self.parse_data_type_for_cast()?;
37540                    self.expect(TokenType::Comma)?;
37541                    let value_type = self.parse_data_type_for_cast()?;
37542                    self.expect(TokenType::RParen)?;
37543                    DataType::Map {
37544                        key_type: Box::new(key_type),
37545                        value_type: Box::new(value_type),
37546                    }
37547                } else if self.match_token(TokenType::LBracket) {
37548                    // Materialize: MAP[TEXT => INT] type syntax
37549                    let key_type = self.parse_data_type_for_cast()?;
37550                    self.expect(TokenType::FArrow)?;
37551                    let value_type = self.parse_data_type_for_cast()?;
37552                    self.expect(TokenType::RBracket)?;
37553                    DataType::Map {
37554                        key_type: Box::new(key_type),
37555                        value_type: Box::new(value_type),
37556                    }
37557                } else {
37558                    DataType::Custom { name }
37559                }
37560            }
37561            "STRUCT" => {
37562                if self.match_token(TokenType::Lt) {
37563                    let fields = self.parse_struct_type_fields(false)?;
37564                    self.expect_gt()?;
37565                    DataType::Struct {
37566                        fields,
37567                        nested: false,
37568                    }
37569                } else if self.match_token(TokenType::LParen) {
37570                    let fields = self.parse_struct_type_fields(true)?;
37571                    self.expect(TokenType::RParen)?;
37572                    DataType::Struct {
37573                        fields,
37574                        nested: true,
37575                    }
37576                } else {
37577                    DataType::Custom { name }
37578                }
37579            }
37580            "ROW" => {
37581                if self.match_token(TokenType::LParen) {
37582                    let fields = self.parse_struct_type_fields(true)?;
37583                    self.expect(TokenType::RParen)?;
37584                    DataType::Struct {
37585                        fields,
37586                        nested: true,
37587                    }
37588                } else {
37589                    DataType::Custom { name }
37590                }
37591            }
37592            "RECORD" => {
37593                // SingleStore RECORD type (like ROW/STRUCT)
37594                if self.match_token(TokenType::LParen) {
37595                    let fields = self.parse_struct_type_fields(true)?;
37596                    self.expect(TokenType::RParen)?;
37597                    DataType::Struct {
37598                        fields,
37599                        nested: true,
37600                    }
37601                } else {
37602                    DataType::Custom { name }
37603                }
37604            }
37605            // Multi-word types that need special handling in cast context
37606            "DOUBLE" => {
37607                // Handle DOUBLE PRECISION
37608                let _ = self.match_identifier("PRECISION");
37609                // ClickHouse/SQL: DOUBLE(precision) or DOUBLE(precision, scale)
37610                let (precision, scale) = if self.match_token(TokenType::LParen) {
37611                    let p = Some(self.expect_number()? as u32);
37612                    let s = if self.match_token(TokenType::Comma) {
37613                        Some(self.expect_number()? as u32)
37614                    } else {
37615                        None
37616                    };
37617                    self.expect(TokenType::RParen)?;
37618                    (p, s)
37619                } else {
37620                    (None, None)
37621                };
37622                DataType::Double { precision, scale }
37623            }
37624            "CHARACTER" | "CHAR" | "NCHAR" => {
37625                // Handle CHARACTER VARYING / CHAR VARYING
37626                if self.match_identifier("VARYING") {
37627                    let length = if self.match_token(TokenType::LParen) {
37628                        let len = Some(self.expect_number()? as u32);
37629                        self.expect(TokenType::RParen)?;
37630                        len
37631                    } else {
37632                        None
37633                    };
37634                    DataType::VarChar {
37635                        length,
37636                        parenthesized_length: false,
37637                    }
37638                } else {
37639                    let length = if self.match_token(TokenType::LParen) {
37640                        let len = Some(self.expect_number()? as u32);
37641                        self.expect(TokenType::RParen)?;
37642                        len
37643                    } else {
37644                        None
37645                    };
37646                    // CHAR CHARACTER SET charset (MySQL CAST context, no length)
37647                    if length.is_none()
37648                        && self.match_identifier("CHARACTER")
37649                        && self.match_token(TokenType::Set)
37650                    {
37651                        let charset = self.expect_identifier_or_keyword()?;
37652                        return Ok(DataType::CharacterSet { name: charset });
37653                    }
37654                    DataType::Char { length }
37655                }
37656            }
37657            "TIME" => {
37658                // Handle TIME(precision) WITH/WITHOUT TIME ZONE
37659                let precision = if self.match_token(TokenType::LParen) {
37660                    let p = Some(self.expect_number()? as u32);
37661                    self.expect(TokenType::RParen)?;
37662                    p
37663                } else {
37664                    None
37665                };
37666                let timezone = if self.match_token(TokenType::With) {
37667                    self.match_keyword("TIME");
37668                    self.match_keyword("ZONE");
37669                    true
37670                } else if self.match_keyword("WITHOUT") {
37671                    self.match_keyword("TIME");
37672                    self.match_keyword("ZONE");
37673                    false
37674                } else {
37675                    false
37676                };
37677                DataType::Time {
37678                    precision,
37679                    timezone,
37680                }
37681            }
37682            "TIMETZ" => {
37683                let precision = if self.match_token(TokenType::LParen) {
37684                    let p = Some(self.expect_number()? as u32);
37685                    self.expect(TokenType::RParen)?;
37686                    p
37687                } else {
37688                    None
37689                };
37690                DataType::Time {
37691                    precision,
37692                    timezone: true,
37693                }
37694            }
37695            "TIMESTAMP" => {
37696                // Handle TIMESTAMP(precision) WITH/WITHOUT TIME ZONE or WITH LOCAL TIME ZONE
37697                let precision = if self.match_token(TokenType::LParen) {
37698                    let p = Some(self.expect_number()? as u32);
37699                    self.expect(TokenType::RParen)?;
37700                    p
37701                } else {
37702                    None
37703                };
37704                // Note: TIME is a keyword (TokenType::Time), so use match_keyword instead of match_identifier
37705                if self.match_token(TokenType::With) {
37706                    // Check for LOCAL TIME ZONE vs TIME ZONE
37707                    if self.match_token(TokenType::Local) {
37708                        self.match_keyword("TIME");
37709                        self.match_keyword("ZONE");
37710                        // TIMESTAMP WITH LOCAL TIME ZONE -> TIMESTAMPLTZ
37711                        DataType::Custom {
37712                            name: "TIMESTAMPLTZ".to_string(),
37713                        }
37714                    } else {
37715                        self.match_keyword("TIME");
37716                        self.match_keyword("ZONE");
37717                        DataType::Timestamp {
37718                            precision,
37719                            timezone: true,
37720                        }
37721                    }
37722                } else if self.match_keyword("WITHOUT") {
37723                    self.match_keyword("TIME");
37724                    self.match_keyword("ZONE");
37725                    DataType::Timestamp {
37726                        precision,
37727                        timezone: false,
37728                    }
37729                } else {
37730                    DataType::Timestamp {
37731                        precision,
37732                        timezone: false,
37733                    }
37734                }
37735            }
37736            "TIMESTAMPTZ" => {
37737                let precision = if self.match_token(TokenType::LParen) {
37738                    let p = self.expect_number()? as u32;
37739                    self.expect(TokenType::RParen)?;
37740                    Some(p)
37741                } else {
37742                    None
37743                };
37744                DataType::Timestamp {
37745                    precision,
37746                    timezone: true,
37747                }
37748            }
37749            "TIMESTAMPLTZ" | "TIMESTAMP_LTZ" => {
37750                let precision = if self.match_token(TokenType::LParen) {
37751                    let p = self.expect_number()? as u32;
37752                    self.expect(TokenType::RParen)?;
37753                    Some(p)
37754                } else {
37755                    None
37756                };
37757                let dt_name = if let Some(p) = precision {
37758                    format!("TIMESTAMPLTZ({})", p)
37759                } else {
37760                    "TIMESTAMPLTZ".to_string()
37761                };
37762                DataType::Custom { name: dt_name }
37763            }
37764            "INTERVAL" => {
37765                // Parse optional unit (DAY, HOUR, etc.) after INTERVAL in cast context
37766                let unit = if (self.check(TokenType::Identifier)
37767                    || self.check(TokenType::Var)
37768                    || self.check_keyword())
37769                    && !self.check(TokenType::RParen)
37770                    && !self.check(TokenType::Comma)
37771                    && !self.check(TokenType::As)
37772                    && !self.check(TokenType::Not)
37773                    && !self.check(TokenType::Null)
37774                {
37775                    Some(self.advance().text.to_ascii_uppercase())
37776                } else {
37777                    None
37778                };
37779                // Parse optional TO unit for range intervals like DAY TO HOUR
37780                let to = if self.match_token(TokenType::To) {
37781                    if self.check(TokenType::Identifier)
37782                        || self.check(TokenType::Var)
37783                        || self.check_keyword()
37784                    {
37785                        Some(self.advance().text.to_ascii_uppercase())
37786                    } else {
37787                        None
37788                    }
37789                } else {
37790                    None
37791                };
37792                DataType::Interval { unit, to }
37793            }
37794            // VARCHAR/NVARCHAR with optional (N) or (MAX) parameter
37795            "VARCHAR" | "NVARCHAR" => {
37796                let is_nvarchar = name == "NVARCHAR";
37797                if self.match_token(TokenType::LParen) {
37798                    if self.check(TokenType::RParen) {
37799                        self.skip();
37800                        DataType::VarChar {
37801                            length: None,
37802                            parenthesized_length: false,
37803                        }
37804                    } else if self.check_identifier("MAX") {
37805                        self.skip();
37806                        self.expect(TokenType::RParen)?;
37807                        let type_name = if is_nvarchar {
37808                            "NVARCHAR(MAX)"
37809                        } else {
37810                            "VARCHAR(MAX)"
37811                        };
37812                        DataType::Custom {
37813                            name: type_name.to_string(),
37814                        }
37815                    } else {
37816                        let n = self.expect_number()? as u32;
37817                        self.expect(TokenType::RParen)?;
37818                        DataType::VarChar {
37819                            length: Some(n),
37820                            parenthesized_length: false,
37821                        }
37822                    }
37823                } else {
37824                    DataType::VarChar {
37825                        length: None,
37826                        parenthesized_length: false,
37827                    }
37828                }
37829            }
37830            // VARBINARY with optional (N) or (MAX) parameter
37831            "VARBINARY" => {
37832                if self.match_token(TokenType::LParen) {
37833                    if self.check(TokenType::RParen) {
37834                        self.skip();
37835                        DataType::VarBinary { length: None }
37836                    } else if self.check_identifier("MAX") {
37837                        self.skip();
37838                        self.expect(TokenType::RParen)?;
37839                        DataType::Custom {
37840                            name: "VARBINARY(MAX)".to_string(),
37841                        }
37842                    } else {
37843                        let n = self.expect_number()? as u32;
37844                        self.expect(TokenType::RParen)?;
37845                        DataType::VarBinary { length: Some(n) }
37846                    }
37847                } else {
37848                    DataType::VarBinary { length: None }
37849                }
37850            }
37851            // DECIMAL/NUMERIC with optional (precision, scale)
37852            "DECIMAL" | "NUMERIC" | "NUMBER" => {
37853                if self.match_token(TokenType::LParen) {
37854                    let precision = Some(self.expect_number()? as u32);
37855                    let scale = if self.match_token(TokenType::Comma) {
37856                        Some(self.expect_number()? as u32)
37857                    } else {
37858                        None
37859                    };
37860                    self.expect(TokenType::RParen)?;
37861                    DataType::Decimal { precision, scale }
37862                } else {
37863                    DataType::Decimal {
37864                        precision: None,
37865                        scale: None,
37866                    }
37867                }
37868            }
37869            // INT/INTEGER/BIGINT/SMALLINT/TINYINT with optional (N) display width
37870            "INT" | "INTEGER" => {
37871                let length = if self.match_token(TokenType::LParen) {
37872                    let n = Some(self.expect_number()? as u32);
37873                    self.expect(TokenType::RParen)?;
37874                    n
37875                } else {
37876                    None
37877                };
37878                DataType::Int {
37879                    length,
37880                    integer_spelling: name == "INTEGER",
37881                }
37882            }
37883            "BIGINT" => {
37884                let length = if self.match_token(TokenType::LParen) {
37885                    let n = Some(self.expect_number()? as u32);
37886                    self.expect(TokenType::RParen)?;
37887                    n
37888                } else {
37889                    None
37890                };
37891                DataType::BigInt { length }
37892            }
37893            "SMALLINT" => {
37894                let length = if self.match_token(TokenType::LParen) {
37895                    let n = Some(self.expect_number()? as u32);
37896                    self.expect(TokenType::RParen)?;
37897                    n
37898                } else {
37899                    None
37900                };
37901                DataType::SmallInt { length }
37902            }
37903            "TINYINT" => {
37904                let length = if self.match_token(TokenType::LParen) {
37905                    let n = Some(self.expect_number()? as u32);
37906                    self.expect(TokenType::RParen)?;
37907                    n
37908                } else {
37909                    None
37910                };
37911                DataType::TinyInt { length }
37912            }
37913            // FLOAT with optional (precision)
37914            "FLOAT" | "REAL" | "BINARY_FLOAT" => {
37915                let (precision, scale) = if self.match_token(TokenType::LParen) {
37916                    let n = Some(self.expect_number()? as u32);
37917                    let s = if self.match_token(TokenType::Comma) {
37918                        Some(self.expect_number()? as u32)
37919                    } else {
37920                        None
37921                    };
37922                    self.expect(TokenType::RParen)?;
37923                    (n, s)
37924                } else {
37925                    (None, None)
37926                };
37927                DataType::Float {
37928                    precision,
37929                    scale,
37930                    real_spelling: name == "REAL",
37931                }
37932            }
37933            "BINARY_DOUBLE" => DataType::Double {
37934                precision: None,
37935                scale: None,
37936            },
37937            // BINARY with optional (length)
37938            "BINARY" => {
37939                let length = if self.match_token(TokenType::LParen) {
37940                    let n = Some(self.expect_number()? as u32);
37941                    self.expect(TokenType::RParen)?;
37942                    n
37943                } else {
37944                    None
37945                };
37946                DataType::Binary { length }
37947            }
37948            // MySQL SIGNED [INTEGER] / UNSIGNED [INTEGER] in CAST context
37949            // CAST(x AS SIGNED INTEGER) -> CAST(x AS SIGNED)
37950            // CAST(x AS UNSIGNED INTEGER) -> CAST(x AS UNSIGNED)
37951            "SIGNED" | "UNSIGNED" => {
37952                // Consume optional INTEGER keyword after SIGNED/UNSIGNED
37953                if self.check_identifier("INTEGER")
37954                    || self.check_keyword_text("INTEGER")
37955                    || self.check_keyword_text("INT")
37956                {
37957                    self.skip();
37958                }
37959                DataType::Custom { name }
37960            }
37961            // ClickHouse Nullable(T) wrapper type
37962            "NULLABLE" => {
37963                self.expect(TokenType::LParen)?;
37964                let inner = self.parse_data_type_for_cast()?;
37965                self.expect(TokenType::RParen)?;
37966                DataType::Nullable {
37967                    inner: Box::new(inner),
37968                }
37969            }
37970            // For simple types, use convert_name_to_type to get proper DataType variants
37971            // This ensures VARCHAR becomes DataType::VarChar, not DataType::Custom
37972            // For user-defined types in generic mode, preserve original case from raw_name
37973            _ => {
37974                let base = self.convert_name_to_type(&name)?;
37975                // ClickHouse: consume parenthesized args for custom types like DateTime('UTC'),
37976                // LowCardinality(String), Variant(String, UInt64), JSON(max_dynamic_paths=8)
37977                if matches!(
37978                    self.config.dialect,
37979                    Some(crate::dialects::DialectType::ClickHouse)
37980                ) && self.check(TokenType::LParen)
37981                    && (matches!(
37982                        base,
37983                        DataType::Custom { .. } | DataType::Json | DataType::JsonB
37984                    ))
37985                {
37986                    self.skip(); // consume (
37987                    let args = self.parse_custom_type_args_balanced()?;
37988                    self.expect(TokenType::RParen)?;
37989                    let base_name = match &base {
37990                        DataType::Json => "JSON".to_string(),
37991                        DataType::JsonB => "JSONB".to_string(),
37992                        DataType::Custom { name } => name.clone(),
37993                        _ => unreachable!(),
37994                    };
37995                    DataType::Custom {
37996                        name: format!("{}({})", base_name, args),
37997                    }
37998                } else if matches!(base, DataType::Custom { .. }) && self.check(TokenType::Dot) {
37999                    // Handle schema-qualified user-defined types (e.g., app.status_enum)
38000                    // by consuming dot-separated identifiers like Python sqlglot's
38001                    // _parse_user_defined_type()
38002                    // Use raw_name to preserve original case for schema-qualified types
38003                    let mut type_name = raw_name.to_string();
38004                    while self.match_token(TokenType::Dot) {
38005                        let tok = self.advance();
38006                        type_name = format!("{}.{}", type_name, tok.text);
38007                    }
38008                    DataType::Custom { name: type_name }
38009                } else if matches!(base, DataType::Custom { .. }) && self.config.dialect.is_none() {
38010                    // Preserve original case for user-defined types in generic mode
38011                    DataType::Custom {
38012                        name: raw_name.to_string(),
38013                    }
38014                } else {
38015                    base
38016                }
38017            }
38018        };
38019
38020        // Materialize: handle postfix LIST syntax (INT LIST, INT LIST LIST LIST)
38021        let is_materialize = matches!(
38022            self.config.dialect,
38023            Some(crate::dialects::DialectType::Materialize)
38024        );
38025        let mut result_type = base_type;
38026        if is_materialize {
38027            while self.check_identifier("LIST") || self.check(TokenType::List) {
38028                self.skip(); // consume LIST
38029                result_type = DataType::List {
38030                    element_type: Box::new(result_type),
38031                };
38032            }
38033        }
38034
38035        // For dialects that support array type suffixes (DuckDB, PostgreSQL, Redshift),
38036        // parse array dimensions. For other dialects, brackets after a cast are subscript operations.
38037        if supports_array_type_suffix {
38038            self.maybe_parse_array_dimensions(result_type)
38039        } else {
38040            Ok(result_type)
38041        }
38042    }
38043
38044    /// Parse custom type arguments with balanced parentheses, preserving nested types
38045    fn parse_custom_type_args_balanced(&mut self) -> Result<String> {
38046        let mut depth = 0usize;
38047        let mut out = String::new();
38048        let mut prev_wordish = false;
38049
38050        while !self.is_at_end() {
38051            if self.check(TokenType::RParen) && depth == 0 {
38052                break;
38053            }
38054
38055            let token = self.advance();
38056            match token.token_type {
38057                TokenType::LParen => {
38058                    out.push('(');
38059                    depth += 1;
38060                    prev_wordish = false;
38061                }
38062                TokenType::RParen => {
38063                    if depth == 0 {
38064                        break;
38065                    }
38066                    depth -= 1;
38067                    out.push(')');
38068                    prev_wordish = true;
38069                }
38070                TokenType::Comma => {
38071                    out.push_str(", ");
38072                    prev_wordish = false;
38073                }
38074                TokenType::Eq => {
38075                    out.push_str(" = ");
38076                    prev_wordish = false;
38077                }
38078                TokenType::Plus => {
38079                    out.push_str(" + ");
38080                    prev_wordish = false;
38081                }
38082                TokenType::Dash => {
38083                    out.push('-');
38084                    prev_wordish = false;
38085                }
38086                TokenType::Dot => {
38087                    out.push('.');
38088                    prev_wordish = false;
38089                }
38090                TokenType::String | TokenType::DollarString => {
38091                    if prev_wordish {
38092                        out.push(' ');
38093                    }
38094                    let escaped = token.text.replace('\'', "''");
38095                    out.push('\'');
38096                    out.push_str(&escaped);
38097                    out.push('\'');
38098                    prev_wordish = true;
38099                }
38100                TokenType::Number | TokenType::Parameter => {
38101                    if prev_wordish {
38102                        out.push(' ');
38103                    }
38104                    out.push_str(&token.text);
38105                    prev_wordish = true;
38106                }
38107                TokenType::QuotedIdentifier => {
38108                    if prev_wordish {
38109                        out.push(' ');
38110                    }
38111                    out.push('"');
38112                    out.push_str(&token.text);
38113                    out.push('"');
38114                    prev_wordish = true;
38115                }
38116                _ => {
38117                    if prev_wordish {
38118                        out.push(' ');
38119                    }
38120                    out.push_str(&token.text);
38121                    prev_wordish = true;
38122                }
38123            }
38124        }
38125
38126        Ok(out)
38127    }
38128
38129    /// Try to parse a data type optionally - returns None if no valid type found
38130    /// Used for JSON_TABLE column definitions where type may or may not be present
38131    fn parse_data_type_optional(&mut self) -> Result<Option<DataType>> {
38132        // Check if current token looks like a type name
38133        if !self.check(TokenType::Identifier)
38134            && !self.check(TokenType::Var)
38135            && !self.check_keyword()
38136        {
38137            return Ok(None);
38138        }
38139
38140        // Don't try to parse PATH as a type
38141        if self.check_identifier("PATH") {
38142            return Ok(None);
38143        }
38144
38145        // ClickHouse: ALIAS, EPHEMERAL, MATERIALIZED are column modifiers, not types
38146        if matches!(
38147            self.config.dialect,
38148            Some(crate::dialects::DialectType::ClickHouse)
38149        ) && (self.check_identifier("ALIAS")
38150            || self.check_identifier("EPHEMERAL")
38151            || self.check(TokenType::Materialized))
38152        {
38153            return Ok(None);
38154        }
38155
38156        let saved_pos = self.current;
38157        match self.parse_data_type() {
38158            Ok(dt) => Ok(Some(dt)),
38159            Err(_) => {
38160                self.current = saved_pos;
38161                Ok(None)
38162            }
38163        }
38164    }
38165
38166    /// Convert a DataType to a string representation for JSONColumnDef.kind
38167    fn data_type_to_string(&self, dt: &DataType) -> String {
38168        match dt {
38169            DataType::Int {
38170                length: Some(n),
38171                integer_spelling: true,
38172            } => format!("INTEGER({})", n),
38173            DataType::Int {
38174                length: Some(n), ..
38175            } => format!("INT({})", n),
38176            DataType::Int {
38177                length: None,
38178                integer_spelling: true,
38179            } => "INTEGER".to_string(),
38180            DataType::Int { length: None, .. } => "INT".to_string(),
38181            DataType::BigInt { length: Some(n) } => format!("BIGINT({})", n),
38182            DataType::BigInt { length: None } => "BIGINT".to_string(),
38183            DataType::SmallInt { length: Some(n) } => format!("SMALLINT({})", n),
38184            DataType::SmallInt { length: None } => "SMALLINT".to_string(),
38185            DataType::TinyInt { length: Some(n) } => format!("TINYINT({})", n),
38186            DataType::TinyInt { length: None } => "TINYINT".to_string(),
38187            DataType::Float {
38188                precision: Some(p),
38189                scale: Some(s),
38190                ..
38191            } => format!("FLOAT({}, {})", p, s),
38192            DataType::Float {
38193                precision: Some(p),
38194                scale: None,
38195                ..
38196            } => format!("FLOAT({})", p),
38197            DataType::Float {
38198                precision: None, ..
38199            } => "FLOAT".to_string(),
38200            DataType::Double {
38201                precision: Some(p),
38202                scale: Some(s),
38203            } => format!("DOUBLE({}, {})", p, s),
38204            DataType::Double {
38205                precision: Some(p),
38206                scale: None,
38207            } => format!("DOUBLE({})", p),
38208            DataType::Double {
38209                precision: None, ..
38210            } => "DOUBLE".to_string(),
38211            DataType::Decimal {
38212                precision: Some(p),
38213                scale: Some(s),
38214            } => format!("DECIMAL({}, {})", p, s),
38215            DataType::Decimal {
38216                precision: Some(p),
38217                scale: None,
38218            } => format!("DECIMAL({})", p),
38219            DataType::Decimal {
38220                precision: None, ..
38221            } => "DECIMAL".to_string(),
38222            DataType::VarChar {
38223                length: Some(n), ..
38224            } => format!("VARCHAR({})", n),
38225            DataType::VarChar { length: None, .. } => "VARCHAR".to_string(),
38226            DataType::Char { length: Some(n) } => format!("CHAR({})", n),
38227            DataType::Char { length: None } => "CHAR".to_string(),
38228            DataType::Text => "TEXT".to_string(),
38229            DataType::Boolean => "BOOLEAN".to_string(),
38230            DataType::Date => "DATE".to_string(),
38231            DataType::Time {
38232                precision: Some(p), ..
38233            } => format!("TIME({})", p),
38234            DataType::Time {
38235                precision: None, ..
38236            } => "TIME".to_string(),
38237            DataType::Timestamp {
38238                precision: Some(p),
38239                timezone: true,
38240            } => format!("TIMESTAMPTZ({})", p),
38241            DataType::Timestamp {
38242                precision: Some(p),
38243                timezone: false,
38244            } => format!("TIMESTAMP({})", p),
38245            DataType::Timestamp {
38246                precision: None,
38247                timezone: true,
38248            } => "TIMESTAMPTZ".to_string(),
38249            DataType::Timestamp {
38250                precision: None,
38251                timezone: false,
38252            } => "TIMESTAMP".to_string(),
38253            DataType::Json => "JSON".to_string(),
38254            DataType::JsonB => "JSONB".to_string(),
38255            DataType::Binary { length: Some(n) } => format!("BINARY({})", n),
38256            DataType::Binary { length: None } => "BINARY".to_string(),
38257            DataType::VarBinary { length: Some(n) } => format!("VARBINARY({})", n),
38258            DataType::VarBinary { length: None } => "VARBINARY".to_string(),
38259            DataType::String { length: Some(n) } => format!("STRING({})", n),
38260            DataType::String { length: None } => "STRING".to_string(),
38261            DataType::Array { element_type, .. } => {
38262                format!("ARRAY({})", self.data_type_to_string(element_type))
38263            }
38264            DataType::Nullable { inner } => {
38265                format!("Nullable({})", self.data_type_to_string(inner))
38266            }
38267            DataType::Custom { name } => name.clone(),
38268            _ => format!("{:?}", dt),
38269        }
38270    }
38271
38272    /// Parse optional array dimensions after a type: [], [N], [N][M], ARRAY, ARRAY[N], etc.
38273    fn maybe_parse_array_dimensions(&mut self, base_type: DataType) -> Result<DataType> {
38274        let mut current_type = base_type;
38275
38276        // Handle PostgreSQL ARRAY keyword suffix: type ARRAY or type ARRAY[3]
38277        if self.check_identifier("ARRAY") {
38278            self.skip(); // consume ARRAY
38279                            // Check for optional dimension: ARRAY[N]
38280            let dimension = if self.match_token(TokenType::LBracket) {
38281                let dim = if self.check(TokenType::Number) {
38282                    let n = self.expect_number()? as u32;
38283                    Some(n)
38284                } else {
38285                    None
38286                };
38287                self.expect(TokenType::RBracket)?;
38288                dim
38289            } else {
38290                None
38291            };
38292            current_type = DataType::Array {
38293                element_type: Box::new(current_type),
38294                dimension,
38295            };
38296        }
38297
38298        // Handle bracket-based array dimensions: TYPE[], TYPE[N], TYPE[][N], etc.
38299        while self.match_token(TokenType::LBracket) {
38300            // Check for optional dimension: [N] or just []
38301            let dimension = if self.check(TokenType::Number) {
38302                let n = self.expect_number()? as u32;
38303                Some(n)
38304            } else {
38305                None
38306            };
38307            self.expect(TokenType::RBracket)?;
38308
38309            current_type = DataType::Array {
38310                element_type: Box::new(current_type),
38311                dimension,
38312            };
38313        }
38314
38315        Ok(current_type)
38316    }
38317
38318    /// Parse spatial type arguments like GEOMETRY(Point, 4326) or GEOGRAPHY
38319    fn parse_spatial_type_args(&mut self) -> Result<(Option<String>, Option<u32>)> {
38320        if self.match_token(TokenType::LParen) {
38321            // First arg can be a subtype name (POINT, LINESTRING, etc.) or a numeric dimension
38322            if self.check(TokenType::Number) {
38323                // Numeric argument (e.g., ST_GEOMETRY(1) in Teradata)
38324                let n = self.expect_number()? as u32;
38325                self.expect(TokenType::RParen)?;
38326                return Ok((None, Some(n)));
38327            }
38328            // Parse subtype
38329            let subtype = Some(self.expect_identifier()?.to_ascii_uppercase());
38330
38331            // Parse optional SRID
38332            let srid = if self.match_token(TokenType::Comma) {
38333                Some(self.expect_number()? as u32)
38334            } else {
38335                None
38336            };
38337
38338            self.expect(TokenType::RParen)?;
38339            Ok((subtype, srid))
38340        } else {
38341            Ok((None, None))
38342        }
38343    }
38344
38345    /// Parse struct/row/union type fields: name TYPE, name TYPE, ...
38346    /// `paren_style` indicates whether we're parsing parenthesized syntax (terminates at RParen)
38347    /// or angle-bracket syntax (terminates at Gt/GtGt).
38348    fn parse_struct_type_fields(&mut self, paren_style: bool) -> Result<Vec<StructField>> {
38349        let mut fields = Vec::new();
38350        // Check for empty field list
38351        if (paren_style && self.check(TokenType::RParen))
38352            || (!paren_style && (self.check(TokenType::Gt) || self.check(TokenType::GtGt)))
38353        {
38354            return Ok(fields);
38355        }
38356        loop {
38357            // Parse field name or just type (for anonymous struct fields)
38358            // Track whether it was a quoted identifier to preserve quoting
38359            let is_quoted = self.check(TokenType::QuotedIdentifier);
38360            let first = self.expect_identifier_or_keyword()?;
38361            let first_upper = first.to_ascii_uppercase();
38362
38363            // Check if this is a parametric type (ARRAY<T>, MAP<K,V>, STRUCT<...>, STRUCT(...))
38364            let is_parametric_type = (first_upper == "ARRAY"
38365                || first_upper == "MAP"
38366                || first_upper == "STRUCT"
38367                || first_upper == "ROW")
38368                && (self.check(TokenType::Lt) || self.check(TokenType::LParen));
38369
38370            let (field_name, field_type) = if is_parametric_type {
38371                // This is a parametric type as an anonymous field
38372                let field_type = self.parse_data_type_from_name(&first_upper)?;
38373                (String::new(), field_type)
38374            } else if self.check(TokenType::Comma)
38375                || self.match_identifier("OPTIONS")  // Check for OPTIONS (but don't consume yet)
38376                || (paren_style && self.check(TokenType::RParen))
38377                || (!paren_style && (self.check(TokenType::Gt) || self.check(TokenType::GtGt)))
38378            {
38379                // Check if we just matched OPTIONS - if so, retreat
38380                if self.previous().text.eq_ignore_ascii_case("OPTIONS") {
38381                    self.current -= 1;
38382                }
38383                // Anonymous field: just a type name
38384                let field_type = self.convert_name_to_type(&first)?;
38385                (String::new(), field_type)
38386            } else if self.is_identifier_token()
38387                || self.is_safe_keyword_as_identifier()
38388                || self.check(TokenType::Lt)
38389                || self.check(TokenType::LParen)
38390                || self.check(TokenType::Colon)
38391            {
38392                // Named field: fieldname TYPE (or fieldname: TYPE for Hive)
38393                // Consume optional colon separator (Hive-style: `STRUCT<field_name: TYPE>`)
38394                self.match_token(TokenType::Colon);
38395                let field_type = self.parse_data_type()?;
38396                // Preserve quoting for field names
38397                let field_name = if is_quoted {
38398                    format!("\"{}\"", first)
38399                } else {
38400                    first
38401                };
38402                (field_name, field_type)
38403            } else {
38404                // Just a type name
38405                let field_type = self.convert_name_to_type(&first)?;
38406                (String::new(), field_type)
38407            };
38408
38409            // Spark/Databricks: Check for COMMENT clause on struct field
38410            let comment = if self.match_token(TokenType::Comment) {
38411                Some(self.expect_string()?)
38412            } else {
38413                None
38414            };
38415
38416            // BigQuery: Check for OPTIONS clause on struct field
38417            let options = if self.match_identifier("OPTIONS") {
38418                self.parse_options_list()?
38419            } else {
38420                Vec::new()
38421            };
38422
38423            fields.push(StructField::with_options_and_comment(
38424                field_name, field_type, options, comment,
38425            ));
38426
38427            if !self.match_token(TokenType::Comma) {
38428                break;
38429            }
38430        }
38431        Ok(fields)
38432    }
38433
38434    /// Parse a data type given a name that was already consumed
38435    /// This is used for standalone type expressions like ARRAY<T>
38436    fn parse_data_type_from_name(&mut self, name: &str) -> Result<DataType> {
38437        match name {
38438            "ARRAY" => {
38439                if self.match_token(TokenType::Lt) {
38440                    let element_type = self.parse_data_type()?;
38441                    self.expect_gt()?;
38442                    Ok(DataType::Array {
38443                        element_type: Box::new(element_type),
38444                        dimension: None,
38445                    })
38446                } else {
38447                    Ok(DataType::Custom {
38448                        name: "ARRAY".to_string(),
38449                    })
38450                }
38451            }
38452            "MAP" => {
38453                if self.match_token(TokenType::Lt) {
38454                    let key_type = self.parse_data_type()?;
38455                    self.expect(TokenType::Comma)?;
38456                    let value_type = self.parse_data_type()?;
38457                    self.expect_gt()?;
38458                    Ok(DataType::Map {
38459                        key_type: Box::new(key_type),
38460                        value_type: Box::new(value_type),
38461                    })
38462                } else {
38463                    Ok(DataType::Custom {
38464                        name: "MAP".to_string(),
38465                    })
38466                }
38467            }
38468            "STRUCT" => {
38469                if self.match_token(TokenType::Lt) {
38470                    let fields = self.parse_struct_type_fields(false)?;
38471                    self.expect_gt()?;
38472                    Ok(DataType::Struct {
38473                        fields,
38474                        nested: false,
38475                    })
38476                } else if self.match_token(TokenType::LParen) {
38477                    let fields = self.parse_struct_type_fields(true)?;
38478                    self.expect(TokenType::RParen)?;
38479                    Ok(DataType::Struct {
38480                        fields,
38481                        nested: true,
38482                    })
38483                } else {
38484                    Ok(DataType::Custom {
38485                        name: "STRUCT".to_string(),
38486                    })
38487                }
38488            }
38489            "ROW" => {
38490                if self.match_token(TokenType::LParen) {
38491                    let fields = self.parse_struct_type_fields(true)?;
38492                    self.expect(TokenType::RParen)?;
38493                    Ok(DataType::Struct {
38494                        fields,
38495                        nested: true,
38496                    })
38497                } else {
38498                    Ok(DataType::Custom {
38499                        name: "ROW".to_string(),
38500                    })
38501                }
38502            }
38503            _ => Ok(DataType::Custom {
38504                name: name.to_string(),
38505            }),
38506        }
38507    }
38508
38509    /// Convert a type name string to a DataType
38510    /// Used for anonymous struct fields where we have just a type name
38511    fn convert_name_to_type(&self, name: &str) -> Result<DataType> {
38512        let upper = name.to_ascii_uppercase();
38513        Ok(match upper.as_str() {
38514            "INT" => DataType::Int {
38515                length: None,
38516                integer_spelling: false,
38517            },
38518            "INTEGER" => DataType::Int {
38519                length: None,
38520                integer_spelling: true,
38521            },
38522            "BIGINT" => DataType::BigInt { length: None },
38523            "SMALLINT" => DataType::SmallInt { length: None },
38524            "TINYINT" => DataType::TinyInt { length: None },
38525            "FLOAT" | "BINARY_FLOAT" => DataType::Float {
38526                precision: None,
38527                scale: None,
38528                real_spelling: false,
38529            },
38530            "REAL" => DataType::Float {
38531                precision: None,
38532                scale: None,
38533                real_spelling: true,
38534            },
38535            "DOUBLE" | "BINARY_DOUBLE" => DataType::Double {
38536                precision: None,
38537                scale: None,
38538            },
38539            "DECIMAL" | "NUMERIC" => DataType::Decimal {
38540                precision: None,
38541                scale: None,
38542            },
38543            "BOOLEAN" | "BOOL" => DataType::Boolean,
38544            "CHAR" | "CHARACTER" | "NCHAR" => DataType::Char { length: None },
38545            "VARCHAR" | "NVARCHAR" => DataType::VarChar {
38546                length: None,
38547                parenthesized_length: false,
38548            },
38549            "TEXT" | "STRING" | "NTEXT" => DataType::Text,
38550            "DATE" => DataType::Date,
38551            "TIME" => DataType::Time {
38552                precision: None,
38553                timezone: false,
38554            },
38555            "TIMETZ" => DataType::Time {
38556                precision: None,
38557                timezone: true,
38558            },
38559            "TIMESTAMP" => DataType::Timestamp {
38560                precision: None,
38561                timezone: false,
38562            },
38563            "INTERVAL" => DataType::Interval {
38564                unit: None,
38565                to: None,
38566            },
38567            "JSON" => DataType::Json,
38568            "JSONB" => DataType::JsonB,
38569            "UUID" => DataType::Uuid,
38570            "BLOB" => DataType::Blob,
38571            "BYTEA" => DataType::VarBinary { length: None },
38572            "BINARY" => DataType::Binary { length: None },
38573            "VARBINARY" => DataType::VarBinary { length: None },
38574            "BIT" => DataType::Bit { length: None },
38575            "VARBIT" => DataType::VarBit { length: None },
38576            _ => DataType::Custom {
38577                name: name.to_string(),
38578            },
38579        })
38580    }
38581
38582    /// Parse star modifiers: EXCLUDE/EXCEPT, REPLACE, RENAME
38583    /// Syntax varies by dialect:
38584    /// - DuckDB: * EXCLUDE (col1, col2)
38585    /// - BigQuery: * EXCEPT (col1, col2), * REPLACE (expr AS col)
38586    /// - Snowflake: * EXCLUDE col, * RENAME (old AS new)
38587    fn parse_star_modifiers(&mut self, table: Option<Identifier>) -> Result<Star> {
38588        self.parse_star_modifiers_with_comments(table, Vec::new())
38589    }
38590
38591    /// Parse star modifiers with explicit trailing comments from the star token
38592    fn parse_star_modifiers_with_comments(
38593        &mut self,
38594        table: Option<Identifier>,
38595        star_trailing_comments: Vec<String>,
38596    ) -> Result<Star> {
38597        let mut except = None;
38598        let mut replace = None;
38599        let mut rename = None;
38600
38601        // Parse EXCLUDE / EXCEPT clause
38602        if self.match_token(TokenType::Exclude) || self.match_token(TokenType::Except) {
38603            // ClickHouse: EXCEPT STRICT col1, col2 (STRICT is optional modifier)
38604            let _ = self.match_text_seq(&["STRICT"]);
38605            let mut columns = Vec::new();
38606            if self.match_token(TokenType::LParen) {
38607                // EXCLUDE (col1, col2) or EXCEPT (A.COL_1, B.COL_2)
38608                loop {
38609                    // ClickHouse: allow string literals in EXCEPT ('col_regex')
38610                    // and keywords like 'key', 'index' as column names
38611                    let col = if self.check(TokenType::String) {
38612                        self.advance().text
38613                    } else if self.is_safe_keyword_as_identifier() {
38614                        self.advance().text
38615                    } else {
38616                        self.expect_identifier()?
38617                    };
38618                    // Handle qualified column names like A.COL_1
38619                    if self.match_token(TokenType::Dot) {
38620                        let subcol = if self.is_safe_keyword_as_identifier() {
38621                            self.advance().text
38622                        } else {
38623                            self.expect_identifier()?
38624                        };
38625                        columns.push(Identifier::new(format!("{}.{}", col, subcol)));
38626                    } else {
38627                        columns.push(Identifier::new(col));
38628                    }
38629                    if !self.match_token(TokenType::Comma) {
38630                        break;
38631                    }
38632                }
38633                self.expect(TokenType::RParen)?;
38634            } else {
38635                // EXCLUDE col (single column, Snowflake) or EXCEPT col1, col2 (ClickHouse)
38636                // or EXCEPT 'regex' (ClickHouse)
38637                loop {
38638                    let col = if self.check(TokenType::String) {
38639                        self.advance().text
38640                    } else if self.is_safe_keyword_as_identifier() {
38641                        self.advance().text
38642                    } else {
38643                        self.expect_identifier()?
38644                    };
38645                    columns.push(Identifier::new(col));
38646                    // ClickHouse allows comma-separated columns without parens: EXCEPT col1, col2
38647                    // But only if the next token after comma looks like a column name
38648                    if !matches!(
38649                        self.config.dialect,
38650                        Some(crate::dialects::DialectType::ClickHouse)
38651                    ) || !self.check(TokenType::Comma)
38652                        || !matches!(
38653                            self.peek_nth(1).map(|t| t.token_type),
38654                            Some(TokenType::Identifier)
38655                                | Some(TokenType::QuotedIdentifier)
38656                                | Some(TokenType::Var)
38657                                | Some(TokenType::String)
38658                        )
38659                    {
38660                        break;
38661                    }
38662                    self.skip(); // consume comma
38663                }
38664            }
38665            except = Some(columns);
38666        }
38667
38668        // Parse REPLACE clause
38669        if self.match_token(TokenType::Replace) {
38670            // ClickHouse: REPLACE STRICT is optional modifier
38671            let _ = self.match_text_seq(&["STRICT"]);
38672            let mut replacements = Vec::new();
38673            if self.match_token(TokenType::LParen) {
38674                loop {
38675                    let expr = self.parse_expression()?;
38676                    self.expect(TokenType::As)?;
38677                    let alias = self.expect_identifier_or_keyword()?;
38678                    replacements.push(Alias::new(expr, Identifier::new(alias)));
38679                    if !self.match_token(TokenType::Comma) {
38680                        break;
38681                    }
38682                }
38683                self.expect(TokenType::RParen)?;
38684            } else if matches!(
38685                self.config.dialect,
38686                Some(crate::dialects::DialectType::ClickHouse)
38687            ) {
38688                // ClickHouse: REPLACE [STRICT] expr AS name (single entry without parens)
38689                // Multiple entries require parens: REPLACE(expr1 AS name1, expr2 AS name2)
38690                let expr = self.parse_expression()?;
38691                self.expect(TokenType::As)?;
38692                let alias = self.expect_identifier_or_keyword()?;
38693                replacements.push(Alias::new(expr, Identifier::new(alias)));
38694            } else {
38695                return Err(self.parse_error("Expected LParen after REPLACE"));
38696            }
38697            replace = Some(replacements);
38698        }
38699
38700        // Parse RENAME clause (Snowflake)
38701        if self.match_token(TokenType::Rename) {
38702            let mut renames = Vec::new();
38703            if self.match_token(TokenType::LParen) {
38704                loop {
38705                    let old_name = self.expect_identifier()?;
38706                    self.expect(TokenType::As)?;
38707                    let new_name = self.expect_identifier()?;
38708                    renames.push((Identifier::new(old_name), Identifier::new(new_name)));
38709                    if !self.match_token(TokenType::Comma) {
38710                        break;
38711                    }
38712                }
38713                self.expect(TokenType::RParen)?;
38714            } else {
38715                // Single rename without parens
38716                let old_name = self.expect_identifier()?;
38717                self.expect(TokenType::As)?;
38718                let new_name = self.expect_identifier()?;
38719                renames.push((Identifier::new(old_name), Identifier::new(new_name)));
38720            }
38721            rename = Some(renames);
38722        }
38723
38724        Ok(Star {
38725            table,
38726            except,
38727            replace,
38728            rename,
38729            trailing_comments: star_trailing_comments,
38730            span: None,
38731        })
38732    }
38733
38734    // === Helper methods ===
38735
38736    /// Check if at end of tokens
38737    #[inline]
38738    fn is_at_end(&self) -> bool {
38739        self.current >= self.tokens.len()
38740    }
38741
38742    /// Check if current token is a query modifier keyword or end of input.
38743    /// Used after GROUP BY ALL/DISTINCT to decide whether to parse expression lists.
38744    fn is_at_query_modifier_or_end(&self) -> bool {
38745        if self.is_at_end() {
38746            return true;
38747        }
38748        matches!(
38749            self.peek().token_type,
38750            TokenType::Having
38751                | TokenType::Qualify
38752                | TokenType::Window
38753                | TokenType::Order
38754                | TokenType::Limit
38755                | TokenType::Fetch
38756                | TokenType::Offset
38757                | TokenType::For
38758                | TokenType::Lock
38759                | TokenType::Union
38760                | TokenType::Except
38761                | TokenType::Intersect
38762                | TokenType::RParen
38763                | TokenType::Semicolon
38764                | TokenType::Where
38765        )
38766    }
38767
38768    /// Create a parse error with position from the current token
38769    fn parse_error(&self, message: impl Into<String>) -> Error {
38770        let span = self.peek().span;
38771        Error::parse(message, span.line, span.column, span.start, span.end)
38772    }
38773
38774    /// Peek at current token
38775    /// Returns reference to current token, or last token if at end
38776    #[inline]
38777    fn peek(&self) -> &Token {
38778        if self.current >= self.tokens.len() {
38779            // Return last token as fallback when at end
38780            // In practice, callers should check is_at_end() before calling peek()
38781            // but this prevents panic
38782            self.tokens.last().expect("Token list should not be empty")
38783        } else {
38784            &self.tokens[self.current]
38785        }
38786    }
38787
38788    /// Look ahead by n positions (0 = current token)
38789    fn peek_nth(&self, n: usize) -> Option<&Token> {
38790        let idx = self.current + n;
38791        if idx < self.tokens.len() {
38792            Some(&self.tokens[idx])
38793        } else {
38794            None
38795        }
38796    }
38797
38798    /// Advance to next token
38799    #[inline]
38800    fn advance(&mut self) -> Token {
38801        if self.current >= self.tokens.len() {
38802            // Return last token as fallback if we're past the end
38803            // In practice, callers should check is_at_end() before calling advance()
38804            return self
38805                .tokens
38806                .last()
38807                .cloned()
38808                .expect("Token list should not be empty");
38809        }
38810        let token = self.tokens[self.current].clone();
38811        self.current += 1;
38812        token
38813    }
38814
38815    /// Advance to next token without returning it (when result is unused)
38816    #[inline]
38817    fn skip(&mut self) {
38818        if self.current < self.tokens.len() {
38819            self.current += 1;
38820        }
38821    }
38822
38823    /// Get the previous token (last consumed)
38824    fn previous(&self) -> &Token {
38825        &self.tokens[self.current - 1]
38826    }
38827
38828    /// Get trailing comments from the previous token
38829    fn previous_trailing_comments(&self) -> &[String] {
38830        if self.current > 0 { &self.tokens[self.current - 1].trailing_comments } else { &[] }
38831    }
38832
38833    /// Get the token type of the previous token (the one before current).
38834    fn previous_token_type(&self) -> Option<TokenType> {
38835        if self.current > 0 {
38836            Some(self.tokens[self.current - 1].token_type.clone())
38837        } else {
38838            None
38839        }
38840    }
38841
38842    /// Wrap a query expression in a Subquery node.
38843    /// Only wraps if the expression is a query statement (Select, Union, etc.),
38844    /// not for simple expressions like column references.
38845    fn maybe_wrap_in_subquery(&self, inner: Expression) -> Expression {
38846        if matches!(
38847            &inner,
38848            Expression::Select(_)
38849                | Expression::Union(_)
38850                | Expression::Intersect(_)
38851                | Expression::Except(_)
38852        ) {
38853            Expression::Subquery(Box::new(Subquery {
38854                this: inner,
38855                alias: None,
38856                column_aliases: Vec::new(),
38857                order_by: None,
38858                limit: None,
38859                offset: None,
38860                distribute_by: None,
38861                sort_by: None,
38862                cluster_by: None,
38863                lateral: false,
38864                modifiers_inside: false,
38865                trailing_comments: Vec::new(),
38866                inferred_type: None,
38867            }))
38868        } else {
38869            inner
38870        }
38871    }
38872
38873    /// Clear trailing_comments from the rightmost leaf of an expression tree.
38874    /// Used by parse_and/parse_or to avoid comment duplication: when the same comment
38875    /// is captured both in an expression's trailing_comments (during parse_primary) and
38876    /// in a BinaryOp's operator_comments (during parse_and/parse_or), we clear the
38877    /// expression's copy since the operator_comments position (after AND/OR) is correct.
38878    fn clear_rightmost_trailing_comments(expr: &mut Expression) {
38879        match expr {
38880            Expression::Column(col) => col.trailing_comments.clear(),
38881            Expression::And(op) | Expression::Or(op) => {
38882                Self::clear_rightmost_trailing_comments(&mut op.right);
38883            }
38884            Expression::Not(op) => {
38885                Self::clear_rightmost_trailing_comments(&mut op.this);
38886            }
38887            // For comparison ops, the rightmost is the right operand
38888            Expression::Eq(op)
38889            | Expression::Neq(op)
38890            | Expression::Lt(op)
38891            | Expression::Lte(op)
38892            | Expression::Gt(op)
38893            | Expression::Gte(op)
38894            | Expression::Add(op)
38895            | Expression::Sub(op)
38896            | Expression::Mul(op)
38897            | Expression::Div(op) => {
38898                Self::clear_rightmost_trailing_comments(&mut op.right);
38899            }
38900            // For other expressions, trailing_comments might be stored differently
38901            // We don't need to handle all variants, just the common ones that appear
38902            // as operands in AND/OR expressions
38903            _ => {}
38904        }
38905    }
38906
38907    /// Get leading comments from the current token (comments that appeared before it)
38908    fn current_leading_comments(&self) -> &[String] {
38909        if !self.is_at_end() { &self.tokens[self.current].comments } else { &[] }
38910    }
38911
38912    /// Convert a slice of tokens to SQL string with proper quoting for strings
38913    fn tokens_to_sql(&self, start: usize, end: usize) -> String {
38914        let mut result = String::new();
38915        let mut prev_line: Option<usize> = None;
38916        let mut prev_end_offset: Option<usize> = None;
38917
38918        for t in &self.tokens[start..end] {
38919            // Check if we moved to a new line (preserve original line structure)
38920            let is_new_line = prev_line.is_some() && t.span.line > prev_line.unwrap();
38921
38922            // Use byte offsets to determine original spacing between tokens.
38923            // This preserves the exact spacing from the source (e.g., TRANSFORM( vs OPTIONS ())
38924            if is_new_line {
38925                result.push('\n');
38926                // Preserve original indentation
38927                // span.column is the column AFTER the last character (1-based),
38928                // so start column = span.column - text.chars().count()
38929                let text_len = t.text.chars().count();
38930                let start_col = t.span.column.saturating_sub(text_len);
38931                // For string tokens, add 2 for the quotes that were stripped
38932                let start_col = if t.token_type == TokenType::String {
38933                    start_col.saturating_sub(2)
38934                } else {
38935                    start_col
38936                };
38937                let indent = if start_col > 1 { start_col - 1 } else { 0 };
38938                for _ in 0..indent {
38939                    result.push(' ');
38940                }
38941            } else if !result.is_empty() {
38942                // Same line: use byte offsets to detect if there was whitespace
38943                let had_space = prev_end_offset.map_or(false, |prev_end| t.span.start > prev_end);
38944                if had_space {
38945                    result.push(' ');
38946                }
38947            }
38948
38949            if t.token_type == TokenType::String {
38950                // Re-add quotes around string literals
38951                result.push('\'');
38952                result.push_str(&t.text.replace('\'', "''"));
38953                result.push('\'');
38954            } else {
38955                result.push_str(&t.text);
38956            }
38957
38958            prev_line = Some(t.span.line);
38959            prev_end_offset = Some(t.span.end);
38960        }
38961        result
38962    }
38963
38964    /// Convert tokens to SQL for CREATE STAGE, normalizing FILE_FORMAT clause
38965    /// Transforms FILE_FORMAT='value' to FILE_FORMAT=(FORMAT_NAME='value')
38966    /// and FILE_FORMAT=schema.format to FILE_FORMAT=(FORMAT_NAME=schema.format)
38967    fn tokens_to_sql_stage_format(&self, start: usize, end: usize) -> String {
38968        let mut result = String::new();
38969        let mut prev_token_type: Option<TokenType> = None;
38970        let mut i = start;
38971
38972        while i < end {
38973            let t = &self.tokens[i];
38974
38975            // Check for FILE_FORMAT= pattern that needs normalization
38976            // FILE_FORMAT must be followed by = and then NOT by (
38977            if (t.token_type == TokenType::Var || t.token_type == TokenType::Identifier)
38978                && t.text.eq_ignore_ascii_case("FILE_FORMAT")
38979                && i + 1 < end
38980                && self.tokens[i + 1].token_type == TokenType::Eq
38981                && (i + 2 >= end || self.tokens[i + 2].token_type != TokenType::LParen)
38982            {
38983                // Need to normalize: FILE_FORMAT=value -> FILE_FORMAT=(FORMAT_NAME=value)
38984                if !result.is_empty() && prev_token_type != Some(TokenType::LParen) {
38985                    result.push(' ');
38986                }
38987                result.push_str("FILE_FORMAT=(FORMAT_NAME=");
38988
38989                // Skip FILE_FORMAT and =
38990                i += 2;
38991
38992                // Collect the value (string literal or qualified identifier like schema.format)
38993                while i < end {
38994                    let val = &self.tokens[i];
38995                    if val.token_type == TokenType::String {
38996                        // String literal: 'format1'
38997                        result.push('\'');
38998                        result.push_str(&val.text.replace('\'', "''"));
38999                        result.push('\'');
39000                        i += 1;
39001                        break;
39002                    } else if val.token_type == TokenType::Var
39003                        || val.token_type == TokenType::Identifier
39004                    {
39005                        // Identifier: schema1 or format1
39006                        result.push_str(&val.text);
39007                        i += 1;
39008                        // Check for dot (qualified name)
39009                        if i < end && self.tokens[i].token_type == TokenType::Dot {
39010                            result.push('.');
39011                            i += 1;
39012                            // Expect identifier after dot
39013                            if i < end {
39014                                result.push_str(&self.tokens[i].text);
39015                                i += 1;
39016                            }
39017                        }
39018                        break;
39019                    } else {
39020                        break;
39021                    }
39022                }
39023                result.push(')');
39024                prev_token_type = Some(TokenType::RParen);
39025                continue;
39026            }
39027
39028            // Normal token handling (same as tokens_to_sql)
39029            let needs_space = !result.is_empty()
39030                && prev_token_type != Some(TokenType::LParen)
39031                && prev_token_type != Some(TokenType::Eq)
39032                && prev_token_type != Some(TokenType::Dot)
39033                && t.token_type != TokenType::Comma
39034                && t.token_type != TokenType::RParen
39035                && t.token_type != TokenType::LParen
39036                && t.token_type != TokenType::Eq
39037                && t.token_type != TokenType::Dot;
39038
39039            if needs_space {
39040                result.push(' ');
39041            }
39042
39043            if t.token_type == TokenType::String {
39044                result.push('\'');
39045                result.push_str(&t.text.replace('\'', "''"));
39046                result.push('\'');
39047            } else {
39048                result.push_str(&t.text);
39049            }
39050
39051            prev_token_type = Some(t.token_type);
39052            i += 1;
39053        }
39054        result
39055    }
39056
39057    /// Like tokens_to_sql but also uppercases keyword tokens and adds space after commas
39058    fn tokens_to_sql_uppercased(&self, start: usize, end: usize) -> String {
39059        let mut result = String::new();
39060        let mut prev_token_type: Option<TokenType> = None;
39061        let mut prev_token_text: Option<String> = None;
39062
39063        for t in &self.tokens[start..end] {
39064            // Smart spacing: no space before comma, ), . or after (, .
39065            // Add space before ( only when preceded by a structural keyword or identifier
39066            // (e.g., "PRIMARY KEY (Id)", "CLUSTERED (EmpID)")
39067            // but NOT after data type keywords (e.g., "VARCHAR(100)", "INT(11)")
39068            let is_lparen_after_keyword = t.token_type == TokenType::LParen
39069                && prev_token_type.map_or(false, |p: TokenType| {
39070                    // Only add space for structural SQL keywords, not data type keywords
39071                    match p {
39072                        TokenType::PrimaryKey | TokenType::ForeignKey | TokenType::Unique
39073                        | TokenType::Check | TokenType::Index | TokenType::Key
39074                        | TokenType::Constraint | TokenType::References
39075                        | TokenType::Not | TokenType::Null
39076                        | TokenType::Default | TokenType::Values | TokenType::In
39077                        | TokenType::Exists | TokenType::Select | TokenType::From
39078                        | TokenType::Where | TokenType::Having | TokenType::Using
39079                        | TokenType::On | TokenType::Set | TokenType::Into
39080                        | TokenType::Table | TokenType::View | TokenType::Create
39081                        | TokenType::Insert | TokenType::Update | TokenType::Delete
39082                        | TokenType::Join | TokenType::Left | TokenType::Right
39083                        | TokenType::Inner | TokenType::Outer | TokenType::Full
39084                        | TokenType::Cross | TokenType::Case | TokenType::When
39085                        | TokenType::Then | TokenType::Else | TokenType::End
39086                        | TokenType::If | TokenType::Partition | TokenType::Over
39087                        | TokenType::Between | TokenType::Like | TokenType::Replace
39088                        | TokenType::Grant | TokenType::Revoke
39089                        => true,
39090                        _ => false,
39091                    }
39092                })
39093                // For Var/Identifier tokens, add space before ( only for structural tokens
39094                // (CLUSTERED, NONCLUSTERED, INDEX) but not data types (VARCHAR, INT, etc.)
39095                || (t.token_type == TokenType::LParen
39096                    && prev_token_text.as_ref().map_or(false, |text| {
39097                        let upper = text.to_ascii_uppercase();
39098                        matches!(upper.as_str(),
39099                            "CLUSTERED" | "NONCLUSTERED" | "HASH" | "RANGE"
39100                            | "INCLUDE" | "FILLFACTOR" | "PAD_INDEX"
39101                        )
39102                    }));
39103            let needs_space = !result.is_empty()
39104                && prev_token_type != Some(TokenType::LParen)
39105                && prev_token_type != Some(TokenType::Dot)
39106                && t.token_type != TokenType::Comma
39107                && t.token_type != TokenType::RParen
39108                && t.token_type != TokenType::Dot
39109                && (t.token_type != TokenType::LParen || is_lparen_after_keyword);
39110
39111            // Add space after comma
39112            if prev_token_type == Some(TokenType::Comma) {
39113                result.push(' ');
39114            } else if needs_space {
39115                result.push(' ');
39116            }
39117
39118            if t.token_type == TokenType::String {
39119                // Re-add quotes around string literals
39120                result.push('\'');
39121                result.push_str(&t.text.replace('\'', "''"));
39122                result.push('\'');
39123            } else if t.token_type.is_keyword() {
39124                // Uppercase keyword tokens
39125                result.push_str(&t.text.to_ascii_uppercase());
39126            } else {
39127                // For non-keyword tokens, preserve original text
39128                result.push_str(&t.text);
39129            }
39130
39131            prev_token_type = Some(t.token_type);
39132            prev_token_text = Some(t.text.clone());
39133        }
39134        result
39135    }
39136
39137    /// Check if current token matches type
39138    #[inline]
39139    fn check(&self, token_type: TokenType) -> bool {
39140        if self.is_at_end() {
39141            false
39142        } else {
39143            self.peek().token_type == token_type
39144        }
39145    }
39146
39147    /// Check if current token is a keyword
39148    fn check_keyword(&self) -> bool {
39149        if self.is_at_end() {
39150            false
39151        } else {
39152            self.peek().token_type.is_keyword()
39153        }
39154    }
39155
39156    /// Check if current UNPIVOT token starts an UNPIVOT clause (vs being an alias).
39157    /// UNPIVOT clause starts with: UNPIVOT(, UNPIVOT INCLUDE, or UNPIVOT EXCLUDE
39158    fn is_unpivot_clause_start(&self) -> bool {
39159        if !self.check(TokenType::Unpivot) {
39160            return false;
39161        }
39162        let next_idx = self.current + 1;
39163        if next_idx >= self.tokens.len() {
39164            return false;
39165        }
39166        let next = &self.tokens[next_idx];
39167        if next.token_type == TokenType::LParen {
39168            return true;
39169        }
39170        // UNPIVOT INCLUDE NULLS (...) or UNPIVOT EXCLUDE NULLS (...)
39171        let next_text = next.text.to_ascii_uppercase();
39172        next_text == "INCLUDE" || next_text == "EXCLUDE"
39173    }
39174
39175    /// Check if current token text matches (case-insensitive), does not advance
39176    fn check_keyword_text(&self, keyword: &str) -> bool {
39177        if self.is_at_end() {
39178            false
39179        } else {
39180            self.peek().text.eq_ignore_ascii_case(keyword)
39181        }
39182    }
39183
39184    /// Check if current token is FROM keyword
39185    fn check_from_keyword(&self) -> bool {
39186        self.check(TokenType::From)
39187    }
39188
39189    /// Check if next token matches type
39190    fn check_next(&self, token_type: TokenType) -> bool {
39191        if self.current + 1 >= self.tokens.len() {
39192            false
39193        } else {
39194            self.tokens[self.current + 1].token_type == token_type
39195        }
39196    }
39197
39198    /// Check if next token is an identifier with specific name (case-insensitive)
39199    fn check_next_identifier(&self, name: &str) -> bool {
39200        if self.current + 1 >= self.tokens.len() {
39201            false
39202        } else {
39203            let token = &self.tokens[self.current + 1];
39204            (token.token_type == TokenType::Var || token.token_type == TokenType::Identifier)
39205                && token.text.eq_ignore_ascii_case(name)
39206        }
39207    }
39208
39209    /// Match an identifier with specific text (case insensitive)
39210    /// Checks for Identifier, Var, and QuotedIdentifier tokens
39211    fn match_identifier(&mut self, text: &str) -> bool {
39212        if (self.check(TokenType::Identifier)
39213            || self.check(TokenType::Var)
39214            || self.check(TokenType::QuotedIdentifier))
39215            && self.peek().text.eq_ignore_ascii_case(text)
39216        {
39217            self.skip();
39218            true
39219        } else {
39220            false
39221        }
39222    }
39223
39224    /// Check if current token is an identifier with specific text (case insensitive)
39225    /// Does NOT advance the parser
39226    fn check_identifier(&self, text: &str) -> bool {
39227        if self.is_at_end() {
39228            return false;
39229        }
39230        (self.check(TokenType::Identifier)
39231            || self.check(TokenType::Var)
39232            || self.check(TokenType::QuotedIdentifier))
39233            && self.peek().text.eq_ignore_ascii_case(text)
39234    }
39235
39236    /// Check if current token is a "safe" keyword that can be used as an identifier.
39237    /// Check if the current Percent token is a PERCENT modifier (not a modulo operator).
39238    /// "PERCENT" spelled out is always a modifier. "%" is a modifier when followed by
39239    /// a clause boundary (OFFSET, end of input, semicolon, RParen, comma, etc.)
39240    fn is_percent_modifier(&self) -> bool {
39241        if self.is_at_end() {
39242            return false;
39243        }
39244        if self.peek().text.eq_ignore_ascii_case("PERCENT") {
39245            return true;
39246        }
39247        // "%" symbol — only treat as PERCENT modifier if followed by a boundary
39248        if self.peek().text == "%" {
39249            let next_idx = self.current + 1;
39250            if next_idx >= self.tokens.len() {
39251                return true; // at end — it's PERCENT
39252            }
39253            let next_type = self.tokens[next_idx].token_type;
39254            return matches!(
39255                next_type,
39256                TokenType::Offset
39257                    | TokenType::Semicolon
39258                    | TokenType::RParen
39259                    | TokenType::From
39260                    | TokenType::Where
39261                    | TokenType::GroupBy
39262                    | TokenType::OrderBy
39263                    | TokenType::Having
39264                    | TokenType::Union
39265                    | TokenType::Intersect
39266                    | TokenType::Except
39267                    | TokenType::Comma
39268                    | TokenType::With // WITH TIES
39269            ) || next_idx >= self.tokens.len();
39270        }
39271        false
39272    }
39273
39274    /// Structural keywords like FROM, WHERE, JOIN, SELECT are NOT safe.
39275    /// Non-structural keywords like FILTER, UPDATE, END, VALUES can be used as identifiers.
39276    fn is_safe_keyword_as_identifier(&self) -> bool {
39277        if self.is_at_end() {
39278            return false;
39279        }
39280        let token_type = self.peek().token_type;
39281        // Structural keywords that should NOT be used as identifiers
39282        let is_structural = matches!(
39283            token_type,
39284            TokenType::From
39285                | TokenType::Where
39286                | TokenType::Select
39287                | TokenType::Insert
39288                | TokenType::Delete
39289                | TokenType::Create
39290                | TokenType::Drop
39291                | TokenType::Alter
39292                | TokenType::Join
39293                | TokenType::Inner
39294                | TokenType::Cross
39295                | TokenType::On
39296                | TokenType::GroupBy
39297                | TokenType::OrderBy
39298                | TokenType::Having
39299                | TokenType::With
39300                | TokenType::Union
39301                | TokenType::Intersect
39302                | TokenType::Except
39303                | TokenType::Qualify
39304                | TokenType::Into
39305                | TokenType::Set
39306                | TokenType::Using
39307                | TokenType::Lateral
39308                | TokenType::Natural
39309        );
39310        // ClickHouse allows many SQL keywords as identifiers (table names, column aliases, etc.)
39311        if matches!(
39312            self.config.dialect,
39313            Some(crate::dialects::DialectType::ClickHouse)
39314        ) {
39315            let is_ch_structural = matches!(
39316                token_type,
39317                TokenType::From
39318                    | TokenType::Where
39319                    | TokenType::Select
39320                    | TokenType::Create
39321                    | TokenType::Drop
39322                    | TokenType::Alter
39323                    | TokenType::On
39324                    | TokenType::GroupBy
39325                    | TokenType::OrderBy
39326                    | TokenType::Having
39327                    | TokenType::With
39328                    | TokenType::Union
39329                    | TokenType::Intersect
39330                    | TokenType::Except
39331                    | TokenType::Into
39332                    | TokenType::Using
39333                    | TokenType::Lateral
39334                    | TokenType::Natural
39335            );
39336            // Also allow certain operator tokens and non-keyword tokens as identifiers
39337            if matches!(token_type, TokenType::RLike | TokenType::Values) {
39338                return true;
39339            }
39340            return self.peek().token_type.is_keyword() && !is_ch_structural;
39341        }
39342        // If it's a keyword but NOT structural, it's safe to use as identifier
39343        self.peek().token_type.is_keyword() && !is_structural
39344    }
39345
39346    /// Check if a token at current position is the last meaningful token in an expression context.
39347    /// This is used to detect when a keyword like IS or KEEP should be treated as an alias
39348    /// instead of an operator keyword.
39349    fn is_last_expression_token(&self, _token_type: TokenType) -> bool {
39350        // Check if the token after the current one is end-of-input or a clause boundary
39351        let next_idx = self.current + 1;
39352        if next_idx >= self.tokens.len() {
39353            return true; // at end of input
39354        }
39355        let next_type = self.tokens[next_idx].token_type;
39356        // Clause boundaries that indicate the current token is the last in the expression
39357        matches!(
39358            next_type,
39359            TokenType::From
39360                | TokenType::Where
39361                | TokenType::GroupBy
39362                | TokenType::OrderBy
39363                | TokenType::Having
39364                | TokenType::Limit
39365                | TokenType::Union
39366                | TokenType::Intersect
39367                | TokenType::Except
39368                | TokenType::Semicolon
39369                | TokenType::RParen
39370                | TokenType::Comma
39371        )
39372    }
39373
39374    /// Check if current token is a type keyword (for lambda type annotations)
39375    fn is_type_keyword(&self) -> bool {
39376        if self.is_at_end() {
39377            return false;
39378        }
39379        let token = self.peek();
39380        // Check for common type keywords that might appear in lambda annotations
39381        // Use text comparison to avoid depending on specific TokenType variants
39382        let text_upper = token.text.to_ascii_uppercase();
39383        matches!(
39384            text_upper.as_str(),
39385            "INT"
39386                | "INTEGER"
39387                | "BIGINT"
39388                | "SMALLINT"
39389                | "TINYINT"
39390                | "DOUBLE"
39391                | "FLOAT"
39392                | "DECIMAL"
39393                | "NUMERIC"
39394                | "REAL"
39395                | "VARCHAR"
39396                | "CHAR"
39397                | "TEXT"
39398                | "STRING"
39399                | "NVARCHAR"
39400                | "NCHAR"
39401                | "BOOLEAN"
39402                | "BOOL"
39403                | "DATE"
39404                | "TIME"
39405                | "TIMESTAMP"
39406                | "DATETIME"
39407                | "INTERVAL"
39408                | "BINARY"
39409                | "VARBINARY"
39410                | "BLOB"
39411                | "ARRAY"
39412                | "MAP"
39413                | "STRUCT"
39414                | "OBJECT"
39415                | "VARIANT"
39416                | "JSON"
39417                | "NUMBER"
39418                | "VARCHAR2"
39419        )
39420    }
39421
39422    /// Check if current token is a command keyword that can safely be used as an implicit alias.
39423    /// This is a narrow set of command-like keywords (GET, PUT, COPY, SHOW, etc.) that are
39424    /// unlikely to conflict with SQL clause keywords when used as implicit aliases.
39425    fn is_command_keyword_as_alias(&self) -> bool {
39426        if self.is_at_end() {
39427            return false;
39428        }
39429        let token_type = self.peek().token_type;
39430        // FORMAT is a query modifier in ClickHouse, so don't treat it as an alias there
39431        if matches!(token_type, TokenType::Format) {
39432            return !matches!(
39433                self.config.dialect,
39434                Some(crate::dialects::DialectType::ClickHouse)
39435            );
39436        }
39437        // Base keywords that can be aliases in all dialects
39438        if matches!(
39439            token_type,
39440            TokenType::Get
39441                | TokenType::Put
39442                | TokenType::Copy
39443                | TokenType::Show
39444                | TokenType::Rename
39445                | TokenType::Enum
39446                | TokenType::Sample
39447                | TokenType::Collate
39448                | TokenType::Add
39449        ) {
39450            return true;
39451        }
39452        // Spark/Hive allow LIMIT and OFFSET as aliases (without quoting)
39453        if matches!(
39454            self.config.dialect,
39455            Some(crate::dialects::DialectType::Spark) | Some(crate::dialects::DialectType::Hive)
39456        ) && matches!(token_type, TokenType::Limit | TokenType::Offset)
39457        {
39458            return true;
39459        }
39460        false
39461    }
39462
39463    /// Check if current token is a keyword that can be used as a table alias.
39464    /// This is more permissive than is_safe_keyword_as_identifier - it allows
39465    /// LEFT, RIGHT, OUTER, FULL which are JOIN keywords but can also be aliases.
39466    fn can_be_alias_keyword(&self) -> bool {
39467        if self.is_at_end() {
39468            return false;
39469        }
39470        let token_type = self.peek().token_type;
39471        // Keywords that can be used as aliases (similar to is_safe_keyword but more permissive)
39472        matches!(
39473            token_type,
39474            TokenType::Left
39475                | TokenType::Right
39476                | TokenType::Outer
39477                | TokenType::Full
39478                | TokenType::Only
39479                | TokenType::Next
39480                | TokenType::All
39481                | TokenType::If
39482        ) || self.is_safe_keyword_as_identifier()
39483    }
39484
39485    /// Match and consume a token type
39486    fn match_token(&mut self, token_type: TokenType) -> bool {
39487        if self.check(token_type) {
39488            self.skip();
39489            true
39490        } else {
39491            false
39492        }
39493    }
39494
39495    /// Match a sequence of keywords
39496    fn match_keywords(&mut self, keywords: &[TokenType]) -> bool {
39497        // Check if all keywords match
39498        for (i, &kw) in keywords.iter().enumerate() {
39499            if self.current + i >= self.tokens.len() {
39500                return false;
39501            }
39502            if self.tokens[self.current + i].token_type != kw {
39503                return false;
39504            }
39505        }
39506
39507        // Consume all matched keywords
39508        self.current += keywords.len();
39509        true
39510    }
39511
39512    /// Expect a specific token type
39513    fn expect(&mut self, token_type: TokenType) -> Result<Token> {
39514        if self.check(token_type) {
39515            Ok(self.advance())
39516        } else {
39517            let got = if self.is_at_end() {
39518                "end of input".to_string()
39519            } else {
39520                format!("{:?}", self.peek().token_type)
39521            };
39522            let got_text = if self.is_at_end() {
39523                "".to_string()
39524            } else {
39525                self.peek().text.clone()
39526            };
39527            let start = self.current.saturating_sub(3);
39528            let end = (self.current + 4).min(self.tokens.len());
39529            let context = self.tokens_to_sql(start, end).replace('\n', " ");
39530            Err(self.parse_error(format!(
39531                "Expected {:?}, got {} ('{}') near [{}]",
39532                token_type, got, got_text, context
39533            )))
39534        }
39535    }
39536
39537    /// Expect a `>` token, handling the case where `>>` was tokenized as GtGt
39538    /// This is needed for parsing nested generic types like `ARRAY<ARRAY<INT>>`
39539    fn expect_gt(&mut self) -> Result<Token> {
39540        if self.check(TokenType::Gt) {
39541            Ok(self.advance())
39542        } else if self.check(TokenType::GtGt) {
39543            // Split >> into two > tokens
39544            // Replace the GtGt with Gt and return a synthetic Gt token
39545            let token = self.peek().clone();
39546            self.tokens[self.current] = Token {
39547                token_type: TokenType::Gt,
39548                text: ">".to_string(),
39549                span: Span {
39550                    start: token.span.start + 1,
39551                    end: token.span.end,
39552                    line: token.span.line,
39553                    column: token.span.column + 1,
39554                },
39555                comments: Vec::new(),
39556                trailing_comments: Vec::new(),
39557            };
39558            Ok(Token {
39559                token_type: TokenType::Gt,
39560                text: ">".to_string(),
39561                span: Span {
39562                    start: token.span.start,
39563                    end: token.span.start + 1,
39564                    line: token.span.line,
39565                    column: token.span.column,
39566                },
39567                comments: token.comments,
39568                trailing_comments: Vec::new(),
39569            })
39570        } else {
39571            Err(self.parse_error(format!(
39572                "Expected Gt, got {:?}",
39573                if self.is_at_end() {
39574                    "end of input".to_string()
39575                } else {
39576                    format!("{:?}", self.peek().token_type)
39577                }
39578            )))
39579        }
39580    }
39581
39582    /// Expect a string literal and return its value
39583    fn expect_string(&mut self) -> Result<String> {
39584        if self.check(TokenType::String) || self.check(TokenType::DollarString) {
39585            Ok(self.advance().text)
39586        } else {
39587            Err(self.parse_error(format!(
39588                "Expected string, got {:?}",
39589                if self.is_at_end() {
39590                    "end of input".to_string()
39591                } else {
39592                    format!("{:?}", self.peek().token_type)
39593                }
39594            )))
39595        }
39596    }
39597
39598    /// Check if the current token is any kind of identifier (regular, quoted, or var)
39599    fn is_identifier_token(&self) -> bool {
39600        self.check(TokenType::Var)
39601            || self.check(TokenType::Identifier)
39602            || self.check(TokenType::QuotedIdentifier)
39603    }
39604
39605    /// Check if current token is a stage reference (starts with @)
39606    /// This handles both DAt token and Var tokens that start with @
39607    fn is_stage_reference(&self) -> bool {
39608        self.check(TokenType::DAt)
39609            || (self.check(TokenType::Var) && self.peek().text.starts_with('@'))
39610    }
39611
39612    /// Check if the current token could be a MySQL numeric-starting identifier (e.g., 00f, 1d)
39613    /// This checks that the Number token is followed by a connected Var/Identifier token
39614    fn is_mysql_numeric_identifier(&self) -> bool {
39615        if !self.check(TokenType::Number)
39616            || !matches!(
39617                self.config.dialect,
39618                Some(crate::dialects::DialectType::MySQL)
39619            )
39620        {
39621            return false;
39622        }
39623        // Check if the next token is connected (no space) and is a var/identifier
39624        if self.current + 1 < self.tokens.len() {
39625            let curr = &self.tokens[self.current];
39626            let next = &self.tokens[self.current + 1];
39627            // Tokens are connected if they are immediately adjacent (no whitespace between)
39628            // span.end is exclusive, so if curr.end == next.start, they are adjacent
39629            let connected = curr.span.end == next.span.start;
39630            connected
39631                && (next.token_type == TokenType::Var || next.token_type == TokenType::Identifier)
39632        } else {
39633            false
39634        }
39635    }
39636
39637    /// Parse a MySQL numeric-starting identifier (e.g., 00f, 1d)
39638    /// Merges the number token with connected identifier tokens
39639    fn parse_mysql_numeric_identifier(&mut self) -> Identifier {
39640        let num_token = self.advance();
39641        let mut name = num_token.text.clone();
39642        // Merge with connected identifier/var tokens
39643        while !self.is_at_end()
39644            && self.is_connected()
39645            && (self.check(TokenType::Var) || self.check(TokenType::Identifier))
39646        {
39647            let tok = self.advance();
39648            name.push_str(&tok.text);
39649        }
39650        Identifier {
39651            name,
39652            // sqlglot treats this as an identifier token and re-emits it quoted.
39653            quoted: true,
39654            trailing_comments: Vec::new(),
39655            span: None,
39656        }
39657    }
39658
39659    /// Check if an uppercase string starting with '_' is a MySQL charset introducer
39660    fn is_mysql_charset_introducer(text: &str) -> bool {
39661        matches!(
39662            text,
39663            "_ARMSCII8"
39664                | "_ASCII"
39665                | "_BIG5"
39666                | "_BINARY"
39667                | "_CP1250"
39668                | "_CP1251"
39669                | "_CP1256"
39670                | "_CP1257"
39671                | "_CP850"
39672                | "_CP852"
39673                | "_CP866"
39674                | "_CP932"
39675                | "_DEC8"
39676                | "_EUCJPMS"
39677                | "_EUCKR"
39678                | "_GB18030"
39679                | "_GB2312"
39680                | "_GBK"
39681                | "_GEOSTD8"
39682                | "_GREEK"
39683                | "_HEBREW"
39684                | "_HP8"
39685                | "_KEYBCS2"
39686                | "_KOI8R"
39687                | "_KOI8U"
39688                | "_LATIN1"
39689                | "_LATIN2"
39690                | "_LATIN5"
39691                | "_LATIN7"
39692                | "_MACCE"
39693                | "_MACROMAN"
39694                | "_SJIS"
39695                | "_SWE7"
39696                | "_TIS620"
39697                | "_UCS2"
39698                | "_UJIS"
39699                | "_UTF8"
39700                | "_UTF16"
39701                | "_UTF16LE"
39702                | "_UTF32"
39703                | "_UTF8MB3"
39704                | "_UTF8MB4"
39705        )
39706    }
39707
39708    /// Check if the current token can be used as an identifier (includes keywords)
39709    fn is_identifier_or_keyword_token(&self) -> bool {
39710        self.is_identifier_token() || self.check_keyword()
39711    }
39712
39713    /// Expect an identifier and return an Identifier struct with quoted flag
39714    fn expect_identifier_with_quoted(&mut self) -> Result<Identifier> {
39715        if self.is_mysql_numeric_identifier() {
39716            return Ok(self.parse_mysql_numeric_identifier());
39717        }
39718        if self.is_identifier_token() {
39719            let token = self.advance();
39720            let quoted = token.token_type == TokenType::QuotedIdentifier;
39721            Ok(Identifier {
39722                name: token.text,
39723                quoted,
39724                trailing_comments: Vec::new(),
39725                span: None,
39726            })
39727        } else if self.check(TokenType::LBrace)
39728            && matches!(
39729                self.config.dialect,
39730                Some(crate::dialects::DialectType::ClickHouse)
39731            )
39732        {
39733            if let Some(param_expr) = self.parse_clickhouse_braced_parameter()? {
39734                if let Expression::Parameter(param) = &param_expr {
39735                    let name = format!(
39736                        "{{{}: {}}}",
39737                        param.name.as_deref().unwrap_or(""),
39738                        param.expression.as_deref().unwrap_or("")
39739                    );
39740                    return Ok(Identifier {
39741                        name,
39742                        quoted: false,
39743                        trailing_comments: Vec::new(),
39744                        span: None,
39745                    });
39746                }
39747            }
39748            Err(self.parse_error("Expected identifier, got LBrace"))
39749        } else {
39750            Err(self.parse_error(format!(
39751                "Expected identifier, got {:?}",
39752                if self.is_at_end() {
39753                    "end of input".to_string()
39754                } else {
39755                    format!("{:?}", self.peek().token_type)
39756                }
39757            )))
39758        }
39759    }
39760
39761    /// Expect an identifier or keyword (for column names, field names, etc.)
39762    fn expect_identifier_or_keyword_with_quoted(&mut self) -> Result<Identifier> {
39763        // MySQL numeric-starting identifiers (e.g., 00f, 1d)
39764        if self.is_mysql_numeric_identifier() {
39765            return Ok(self.parse_mysql_numeric_identifier());
39766        }
39767        // Also accept ? (Parameter) as an identifier placeholder
39768        // For positional parameters like $23, the token text is "23" (without $)
39769        if self.check(TokenType::Parameter) {
39770            let token = self.advance();
39771            // If the text is a number, it's a positional parameter like $1, $2, $23
39772            // Construct $N as the identifier name
39773            let name = if token.text.chars().all(|c| c.is_ascii_digit()) && !token.text.is_empty() {
39774                format!("${}", token.text)
39775            } else {
39776                // Plain ? placeholder or other parameter
39777                "?".to_string()
39778            };
39779            return Ok(Identifier {
39780                name,
39781                quoted: false,
39782                trailing_comments: Vec::new(),
39783                span: None,
39784            });
39785        }
39786        if self.is_identifier_or_keyword_token() {
39787            let token = self.advance();
39788            let quoted = token.token_type == TokenType::QuotedIdentifier;
39789            Ok(Identifier {
39790                name: token.text,
39791                quoted,
39792                trailing_comments: Vec::new(),
39793                span: None,
39794            })
39795        } else if self.check(TokenType::LBrace)
39796            && matches!(
39797                self.config.dialect,
39798                Some(crate::dialects::DialectType::ClickHouse)
39799            )
39800        {
39801            // ClickHouse query parameter: {name:Type}
39802            if let Some(param_expr) = self.parse_clickhouse_braced_parameter()? {
39803                // Extract the parameter name to use as the identifier
39804                if let Expression::Parameter(param) = &param_expr {
39805                    let name = format!(
39806                        "{{{}: {}}}",
39807                        param.name.as_deref().unwrap_or(""),
39808                        param.expression.as_deref().unwrap_or("")
39809                    );
39810                    return Ok(Identifier {
39811                        name,
39812                        quoted: false,
39813                        trailing_comments: Vec::new(),
39814                        span: None,
39815                    });
39816                }
39817            }
39818            Err(self.parse_error("Expected identifier, got LBrace"))
39819        } else {
39820            Err(self.parse_error(format!(
39821                "Expected identifier, got {:?}",
39822                if self.is_at_end() {
39823                    "end of input".to_string()
39824                } else {
39825                    format!("{:?}", self.peek().token_type)
39826                }
39827            )))
39828        }
39829    }
39830
39831    /// Expect an identifier
39832    fn expect_identifier(&mut self) -> Result<String> {
39833        if self.is_identifier_token() {
39834            Ok(self.advance().text)
39835        } else if self.check(TokenType::LBrace)
39836            && matches!(
39837                self.config.dialect,
39838                Some(crate::dialects::DialectType::ClickHouse)
39839            )
39840        {
39841            if let Some(param_expr) = self.parse_clickhouse_braced_parameter()? {
39842                if let Expression::Parameter(param) = &param_expr {
39843                    return Ok(format!(
39844                        "{{{}: {}}}",
39845                        param.name.as_deref().unwrap_or(""),
39846                        param.expression.as_deref().unwrap_or("")
39847                    ));
39848                }
39849            }
39850            Err(self.parse_error("Expected identifier, got LBrace"))
39851        } else {
39852            Err(self.parse_error(format!(
39853                "Expected identifier, got {:?}",
39854                if self.is_at_end() {
39855                    "end of input".to_string()
39856                } else {
39857                    format!("{:?}", self.peek().token_type)
39858                }
39859            )))
39860        }
39861    }
39862
39863    /// Expect an identifier or keyword (for aliases, column names, etc.)
39864    fn expect_identifier_or_keyword(&mut self) -> Result<String> {
39865        if self.is_identifier_or_keyword_token() {
39866            Ok(self.advance().text)
39867        } else if self.check(TokenType::LBrace)
39868            && matches!(
39869                self.config.dialect,
39870                Some(crate::dialects::DialectType::ClickHouse)
39871            )
39872        {
39873            if let Some(param_expr) = self.parse_clickhouse_braced_parameter()? {
39874                if let Expression::Parameter(param) = &param_expr {
39875                    return Ok(format!(
39876                        "{{{}: {}}}",
39877                        param.name.as_deref().unwrap_or(""),
39878                        param.expression.as_deref().unwrap_or("")
39879                    ));
39880                }
39881            }
39882            Err(self.parse_error("Expected identifier, got LBrace"))
39883        } else {
39884            Err(self.parse_error(format!(
39885                "Expected identifier, got {:?}",
39886                if self.is_at_end() {
39887                    "end of input".to_string()
39888                } else {
39889                    format!("{:?}", self.peek().token_type)
39890                }
39891            )))
39892        }
39893    }
39894
39895    /// Expect an identifier or safe keyword (for CTE names, column names in CREATE TABLE, etc.)
39896    /// This is more permissive than expect_identifier but excludes structural keywords
39897    fn expect_identifier_or_safe_keyword(&mut self) -> Result<String> {
39898        if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
39899            Ok(self.advance().text)
39900        } else if self.check(TokenType::LBrace)
39901            && matches!(
39902                self.config.dialect,
39903                Some(crate::dialects::DialectType::ClickHouse)
39904            )
39905        {
39906            if let Some(param_expr) = self.parse_clickhouse_braced_parameter()? {
39907                if let Expression::Parameter(param) = &param_expr {
39908                    return Ok(format!(
39909                        "{{{}: {}}}",
39910                        param.name.as_deref().unwrap_or(""),
39911                        param.expression.as_deref().unwrap_or("")
39912                    ));
39913                }
39914            }
39915            Err(self.parse_error("Expected identifier, got LBrace"))
39916        } else {
39917            Err(self.parse_error(format!(
39918                "Expected identifier, got {:?}",
39919                if self.is_at_end() {
39920                    "end of input".to_string()
39921                } else {
39922                    format!("{:?}", self.peek().token_type)
39923                }
39924            )))
39925        }
39926    }
39927
39928    /// Expect an identifier or safe keyword, preserving quoted flag
39929    fn expect_identifier_or_safe_keyword_with_quoted(&mut self) -> Result<Identifier> {
39930        if self.is_mysql_numeric_identifier() {
39931            return Ok(self.parse_mysql_numeric_identifier());
39932        }
39933        if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
39934            let token = self.advance();
39935            let quoted = token.token_type == TokenType::QuotedIdentifier;
39936            Ok(Identifier {
39937                name: token.text,
39938                quoted,
39939                trailing_comments: Vec::new(),
39940                span: None,
39941            })
39942        } else {
39943            Err(self.parse_error(format!(
39944                "Expected identifier, got {:?}",
39945                if self.is_at_end() {
39946                    "end of input".to_string()
39947                } else {
39948                    format!("{:?}", self.peek().token_type)
39949                }
39950            )))
39951        }
39952    }
39953
39954    fn expect_identifier_or_alias_keyword_with_quoted(&mut self) -> Result<Identifier> {
39955        // ClickHouse: any keyword can be used as a table alias after explicit AS
39956        let ch_keyword = matches!(
39957            self.config.dialect,
39958            Some(crate::dialects::DialectType::ClickHouse)
39959        ) && self.peek().token_type.is_keyword();
39960        if self.is_identifier_token()
39961            || self.can_be_alias_keyword()
39962            || self.is_safe_keyword_as_identifier()
39963            || ch_keyword
39964        {
39965            let token = self.advance();
39966            let quoted = token.token_type == TokenType::QuotedIdentifier;
39967            Ok(Identifier {
39968                name: token.text,
39969                quoted,
39970                trailing_comments: Vec::new(),
39971                span: None,
39972            })
39973        } else if self.check(TokenType::String)
39974            && matches!(
39975                self.config.dialect,
39976                Some(crate::dialects::DialectType::DuckDB)
39977            )
39978        {
39979            // DuckDB allows string literals as identifiers (e.g., WITH 'x' AS (...))
39980            let token = self.advance();
39981            Ok(Identifier {
39982                name: token.text,
39983                quoted: true,
39984                trailing_comments: Vec::new(),
39985                span: None,
39986            })
39987        } else {
39988            Err(self.parse_error(format!(
39989                "Expected identifier, got {:?}",
39990                if self.is_at_end() {
39991                    "end of input".to_string()
39992                } else {
39993                    format!("{:?}", self.peek().token_type)
39994                }
39995            )))
39996        }
39997    }
39998
39999    /// Expect a number
40000    fn expect_number(&mut self) -> Result<i64> {
40001        let negative = self.match_token(TokenType::Dash);
40002        if self.check(TokenType::Number) {
40003            let text = self.advance().text;
40004            let val = text
40005                .parse::<i64>()
40006                .map_err(|_| self.parse_error(format!("Invalid number: {}", text)))?;
40007            Ok(if negative { -val } else { val })
40008        } else {
40009            Err(self.parse_error("Expected number"))
40010        }
40011    }
40012
40013    /// Parse a comma-separated list of expressions.
40014    /// Supports named arguments with => or := syntax.
40015    fn parse_expression_list_with_capacity(
40016        &mut self,
40017        capacity_hint: usize,
40018    ) -> Result<Vec<Expression>> {
40019        let mut expressions = Vec::with_capacity(capacity_hint);
40020
40021        loop {
40022            // Check if this is a named argument: identifier => value or identifier := value
40023            // Also check for safe keywords (like TYPE, FORMAT, etc.) that can be used as named arg names
40024            let expr = if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
40025                let start_pos = self.current;
40026                let name = self.expect_identifier_or_keyword_with_quoted()?;
40027
40028                if self.match_token(TokenType::FArrow) {
40029                    // name => value
40030                    let value = self.parse_expression()?;
40031                    Expression::NamedArgument(Box::new(NamedArgument {
40032                        name,
40033                        value,
40034                        separator: NamedArgSeparator::DArrow,
40035                    }))
40036                } else if self.match_token(TokenType::ColonEq) {
40037                    // name := value
40038                    let value = self.parse_expression()?;
40039                    Expression::NamedArgument(Box::new(NamedArgument {
40040                        name,
40041                        value,
40042                        separator: NamedArgSeparator::ColonEq,
40043                    }))
40044                } else {
40045                    // Not a named argument, backtrack and parse as regular expression
40046                    self.current = start_pos;
40047                    self.parse_expression()?
40048                }
40049            } else {
40050                self.parse_expression()?
40051            };
40052
40053            // Check for AS alias on this expression (Spark/Hive: IF(cond, val AS name, ...))
40054            let expr = if self.check(TokenType::As) {
40055                let as_pos = self.current;
40056                self.skip(); // consume AS
40057                                // Check if what follows looks like an alias name
40058                if self.is_identifier_token()
40059                    || self.is_safe_keyword_as_identifier()
40060                    || (matches!(
40061                        self.config.dialect,
40062                        Some(crate::dialects::DialectType::ClickHouse)
40063                    ) && self.peek().token_type.is_keyword())
40064                {
40065                    let alias = self.expect_identifier_or_keyword_with_quoted()?;
40066                    let alias_expr = Expression::Alias(Box::new(Alias {
40067                        this: expr,
40068                        alias,
40069                        column_aliases: Vec::new(),
40070                        pre_alias_comments: Vec::new(),
40071                        trailing_comments: Vec::new(),
40072                        inferred_type: None,
40073                    }));
40074                    // ClickHouse: if followed by an operator, the alias is part of a bigger expression
40075                    // e.g., blockSize() AS bs < 1000 means (blockSize() AS bs) < 1000
40076                    if matches!(
40077                        self.config.dialect,
40078                        Some(crate::dialects::DialectType::ClickHouse)
40079                    ) && matches!(
40080                        self.peek().token_type,
40081                        TokenType::Lt
40082                            | TokenType::Gt
40083                            | TokenType::Lte
40084                            | TokenType::Gte
40085                            | TokenType::Eq
40086                            | TokenType::Neq
40087                            | TokenType::Plus
40088                            | TokenType::Dash
40089                            | TokenType::Star
40090                            | TokenType::Slash
40091                            | TokenType::Percent
40092                            | TokenType::And
40093                            | TokenType::Or
40094                            | TokenType::Like
40095                            | TokenType::Not
40096                            | TokenType::In
40097                            | TokenType::Is
40098                            | TokenType::Between
40099                    ) {
40100                        // Parse the operator and right-hand side
40101                        let op_token = self.advance();
40102                        let right = self.parse_expression()?;
40103                        match op_token.token_type {
40104                            TokenType::Lt => {
40105                                Expression::Lt(Box::new(BinaryOp::new(alias_expr, right)))
40106                            }
40107                            TokenType::Gt => {
40108                                Expression::Gt(Box::new(BinaryOp::new(alias_expr, right)))
40109                            }
40110                            TokenType::Lte => {
40111                                Expression::Lte(Box::new(BinaryOp::new(alias_expr, right)))
40112                            }
40113                            TokenType::Gte => {
40114                                Expression::Gte(Box::new(BinaryOp::new(alias_expr, right)))
40115                            }
40116                            TokenType::Eq => {
40117                                Expression::Eq(Box::new(BinaryOp::new(alias_expr, right)))
40118                            }
40119                            TokenType::Neq => {
40120                                Expression::Neq(Box::new(BinaryOp::new(alias_expr, right)))
40121                            }
40122                            TokenType::Plus => {
40123                                Expression::Add(Box::new(BinaryOp::new(alias_expr, right)))
40124                            }
40125                            TokenType::Dash => {
40126                                Expression::Sub(Box::new(BinaryOp::new(alias_expr, right)))
40127                            }
40128                            TokenType::Star => {
40129                                Expression::Mul(Box::new(BinaryOp::new(alias_expr, right)))
40130                            }
40131                            TokenType::Slash => {
40132                                Expression::Div(Box::new(BinaryOp::new(alias_expr, right)))
40133                            }
40134                            TokenType::Percent => {
40135                                Expression::Mod(Box::new(BinaryOp::new(alias_expr, right)))
40136                            }
40137                            TokenType::And => {
40138                                Expression::And(Box::new(BinaryOp::new(alias_expr, right)))
40139                            }
40140                            TokenType::Or => {
40141                                Expression::Or(Box::new(BinaryOp::new(alias_expr, right)))
40142                            }
40143                            _ => alias_expr, // fallback, shouldn't happen
40144                        }
40145                    } else {
40146                        alias_expr
40147                    }
40148                } else {
40149                    // Not an alias name, backtrack
40150                    self.current = as_pos;
40151                    expr
40152                }
40153            } else {
40154                expr
40155            };
40156
40157            // Check for trailing comments on this expression
40158            // Only wrap in Annotated for expression types that don't have their own trailing_comments field
40159            let trailing_comments = self.previous_trailing_comments().to_vec();
40160            let expr = if trailing_comments.is_empty() {
40161                expr
40162            } else {
40163                // Only annotate Literals and other types that don't capture trailing comments
40164                match &expr {
40165                    Expression::Literal(_) | Expression::Boolean(_) | Expression::Null(_) => {
40166                        Expression::Annotated(Box::new(Annotated {
40167                            this: expr,
40168                            trailing_comments,
40169                        }))
40170                    }
40171                    // For expressions that already capture trailing_comments, don't double-wrap
40172                    _ => expr,
40173                }
40174            };
40175            expressions.push(expr);
40176
40177            if !self.match_token(TokenType::Comma) {
40178                break;
40179            }
40180            // ClickHouse: allow trailing comma before RParen in expression lists
40181            if matches!(
40182                self.config.dialect,
40183                Some(crate::dialects::DialectType::ClickHouse)
40184            ) && self.check(TokenType::RParen)
40185            {
40186                break;
40187            }
40188        }
40189
40190        Ok(expressions)
40191    }
40192
40193    /// Parse a comma-separated list of expressions.
40194    /// Supports named arguments with => or := syntax.
40195    fn parse_expression_list(&mut self) -> Result<Vec<Expression>> {
40196        self.parse_expression_list_with_capacity(0)
40197    }
40198
40199    /// Estimate top-level expression count until the next unmatched `)`.
40200    ///
40201    /// This is used for pre-allocating comma-separated lists like `IN (...)`
40202    /// to reduce `Vec` growth churn on very large lists.
40203    fn estimate_expression_list_capacity_until_rparen(&self) -> usize {
40204        if self.current >= self.tokens.len() || self.check(TokenType::RParen) {
40205            return 0;
40206        }
40207
40208        let mut idx = self.current;
40209        let mut paren_depth = 0usize;
40210        let mut bracket_depth = 0usize;
40211        let mut brace_depth = 0usize;
40212        let mut commas = 0usize;
40213        let mut has_any_token = false;
40214
40215        while idx < self.tokens.len() {
40216            let token_type = self.tokens[idx].token_type;
40217            match token_type {
40218                TokenType::LParen => paren_depth += 1,
40219                TokenType::RParen => {
40220                    if paren_depth == 0 && bracket_depth == 0 && brace_depth == 0 {
40221                        break;
40222                    }
40223                    paren_depth = paren_depth.saturating_sub(1);
40224                }
40225                TokenType::LBracket => bracket_depth += 1,
40226                TokenType::RBracket => bracket_depth = bracket_depth.saturating_sub(1),
40227                TokenType::LBrace => brace_depth += 1,
40228                TokenType::RBrace => brace_depth = brace_depth.saturating_sub(1),
40229                TokenType::Comma if paren_depth == 0 && bracket_depth == 0 && brace_depth == 0 => {
40230                    commas += 1;
40231                }
40232                _ => {}
40233            }
40234            has_any_token = true;
40235            idx += 1;
40236        }
40237
40238        if has_any_token {
40239            commas + 1
40240        } else {
40241            0
40242        }
40243    }
40244
40245    /// Parse function arguments with lambda support (for TRANSFORM and similar functions).
40246    /// Handles Snowflake typed lambda syntax: `a int -> a + 1`
40247    fn parse_function_args_with_lambda(&mut self) -> Result<Vec<Expression>> {
40248        let mut expressions = Vec::new();
40249
40250        loop {
40251            // Try to detect typed lambda: identifier type -> body
40252            let expr = if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
40253                let saved_pos = self.current;
40254                let ident_token = self.advance();
40255                let ident_name = ident_token.text.clone();
40256
40257                // Check for arrow (simple lambda: a -> body)
40258                if self.match_token(TokenType::Arrow) {
40259                    let body = self.parse_expression()?;
40260                    Expression::Lambda(Box::new(LambdaExpr {
40261                        parameters: vec![Identifier::new(ident_name)],
40262                        body,
40263                        colon: false,
40264                        parameter_types: Vec::new(),
40265                    }))
40266                }
40267                // Check for type annotation followed by arrow: a int -> body
40268                else if !self.is_at_end()
40269                    && self.is_type_keyword()
40270                    && !self.check(TokenType::FArrow)
40271                    && !self.check(TokenType::ColonEq)
40272                {
40273                    let type_annotation = self.parse_data_type()?;
40274                    if self.match_token(TokenType::Arrow) {
40275                        let body = self.parse_expression()?;
40276                        Expression::Lambda(Box::new(LambdaExpr {
40277                            parameters: vec![Identifier::new(ident_name)],
40278                            body,
40279                            colon: false,
40280                            parameter_types: vec![Some(type_annotation)],
40281                        }))
40282                    } else {
40283                        self.current = saved_pos;
40284                        self.parse_expression()?
40285                    }
40286                } else {
40287                    // Not a lambda, backtrack and parse as regular expression
40288                    self.current = saved_pos;
40289                    self.parse_expression()?
40290                }
40291            } else {
40292                self.parse_expression()?
40293            };
40294
40295            expressions.push(expr);
40296            if !self.match_token(TokenType::Comma) {
40297                break;
40298            }
40299        }
40300
40301        Ok(expressions)
40302    }
40303
40304    /// Parse a comma-separated list of expressions for VALUES tuples
40305    /// This variant supports AS aliases on each element (Hive syntax): VALUES (1 AS a, 2 AS b, 3)
40306    fn parse_values_expression_list(&mut self) -> Result<Vec<Expression>> {
40307        let mut expressions = Vec::new();
40308
40309        loop {
40310            // Handle DEFAULT keyword in VALUES - output as unquoted Var (like Python sqlglot's exp.var("DEFAULT"))
40311            let expr = if self.match_token(TokenType::Default) {
40312                Expression::Var(Box::new(crate::expressions::Var {
40313                    this: "DEFAULT".to_string(),
40314                }))
40315            } else {
40316                self.parse_expression()?
40317            };
40318
40319            // Capture trailing comments on the expression (e.g., `1 /* c4 */`)
40320            let trailing_comments = self.previous_trailing_comments().to_vec();
40321            let expr = if !trailing_comments.is_empty() {
40322                match &expr {
40323                    Expression::Literal(_) | Expression::Boolean(_) | Expression::Null(_) => {
40324                        Expression::Annotated(Box::new(crate::expressions::Annotated {
40325                            this: expr,
40326                            trailing_comments,
40327                        }))
40328                    }
40329                    _ => expr,
40330                }
40331            } else {
40332                expr
40333            };
40334
40335            // Check for AS alias on this value element (Hive syntax)
40336            let expr_with_alias = if self.match_token(TokenType::As) {
40337                let alias = self.expect_identifier_or_keyword_with_quoted()?;
40338                Expression::Alias(Box::new(Alias::new(expr, alias)))
40339            } else {
40340                expr
40341            };
40342
40343            expressions.push(expr_with_alias);
40344
40345            if !self.match_token(TokenType::Comma) {
40346                break;
40347            }
40348            // ClickHouse: trailing comma in VALUES, e.g., (1, 2, 3,)
40349            if self.check(TokenType::RParen) {
40350                break;
40351            }
40352        }
40353
40354        Ok(expressions)
40355    }
40356
40357    /// Parse a comma-separated list of identifiers
40358    fn parse_identifier_list(&mut self) -> Result<Vec<Identifier>> {
40359        let mut identifiers = Vec::new();
40360
40361        loop {
40362            // Allow keywords as identifiers in identifier lists (e.g., CTE column aliases)
40363            // Check if it's a quoted identifier before consuming
40364            let quoted = self.check(TokenType::QuotedIdentifier);
40365            let mut name = self.expect_identifier_or_safe_keyword()?;
40366            // ClickHouse: handle dotted names in identifier lists (e.g., INSERT INTO t (n.a, n.b))
40367            // Use keyword_with_quoted to allow any keyword after dot (e.g., replace.from)
40368            if matches!(
40369                self.config.dialect,
40370                Some(crate::dialects::DialectType::ClickHouse)
40371            ) {
40372                while self.match_token(TokenType::Dot) {
40373                    let sub_id = self.expect_identifier_or_keyword_with_quoted()?;
40374                    name = format!("{}.{}", name, sub_id.name);
40375                }
40376            }
40377            let trailing_comments = self.previous_trailing_comments().to_vec();
40378            identifiers.push(Identifier {
40379                name,
40380                quoted,
40381                trailing_comments,
40382                span: None,
40383            });
40384
40385            if !self.match_token(TokenType::Comma) {
40386                break;
40387            }
40388            // ClickHouse: allow trailing comma before RParen in identifier lists
40389            if matches!(
40390                self.config.dialect,
40391                Some(crate::dialects::DialectType::ClickHouse)
40392            ) && self.check(TokenType::RParen)
40393            {
40394                break;
40395            }
40396        }
40397
40398        Ok(identifiers)
40399    }
40400
40401    /// Parse a comma-separated list of column references for USING clause
40402    /// Supports qualified names like table.col but extracts only the column part
40403    fn parse_using_column_list(&mut self) -> Result<Vec<Identifier>> {
40404        let mut identifiers = Vec::new();
40405
40406        loop {
40407            // ClickHouse: USING * — wildcard in USING clause
40408            if matches!(
40409                self.config.dialect,
40410                Some(crate::dialects::DialectType::ClickHouse)
40411            ) && self.match_token(TokenType::Star)
40412            {
40413                identifiers.push(Identifier::new("*".to_string()));
40414                if !self.match_token(TokenType::Comma) {
40415                    break;
40416                }
40417                continue;
40418            }
40419            // Check if it's a quoted identifier before consuming
40420            let quoted = self.check(TokenType::QuotedIdentifier);
40421            let mut name = self.expect_identifier_or_safe_keyword()?;
40422            let mut final_quoted = quoted;
40423
40424            // Handle qualified names: table.column or schema.table.column
40425            // Keep only the final column name
40426            while self.match_token(TokenType::Dot) {
40427                final_quoted = self.check(TokenType::QuotedIdentifier);
40428                name = self.expect_identifier_or_safe_keyword()?;
40429            }
40430
40431            // ClickHouse: USING (col AS alias) — consume optional AS alias
40432            if matches!(
40433                self.config.dialect,
40434                Some(crate::dialects::DialectType::ClickHouse)
40435            ) && self.match_token(TokenType::As)
40436            {
40437                // Use the alias name instead
40438                final_quoted = self.check(TokenType::QuotedIdentifier);
40439                name = self.expect_identifier_or_safe_keyword()?;
40440            }
40441
40442            let trailing_comments = self.previous_trailing_comments().to_vec();
40443            identifiers.push(Identifier {
40444                name,
40445                quoted: final_quoted,
40446                trailing_comments,
40447                span: None,
40448            });
40449
40450            if !self.match_token(TokenType::Comma) {
40451                break;
40452            }
40453        }
40454
40455        Ok(identifiers)
40456    }
40457
40458    /// Parse a comma-separated list of identifiers for index columns.
40459    /// Supports MySQL prefix lengths: col(16) and sort order: col DESC
40460    fn parse_index_identifier_list(&mut self) -> Result<Vec<Identifier>> {
40461        let mut identifiers = Vec::new();
40462
40463        loop {
40464            let quoted = self.check(TokenType::QuotedIdentifier);
40465            let name = self.expect_identifier_or_safe_keyword()?;
40466            let trailing_comments = self.previous_trailing_comments().to_vec();
40467
40468            // Check for prefix length: col(16)
40469            let mut display_name = name.clone();
40470            if self.match_token(TokenType::LParen) {
40471                if self.check(TokenType::Number) {
40472                    let len = self.advance().text;
40473                    display_name = format!("{}({})", name, len);
40474                }
40475                self.expect(TokenType::RParen)?;
40476            }
40477
40478            // Check for DESC/ASC sort order
40479            if self.match_token(TokenType::Desc) {
40480                display_name = format!("{} DESC", display_name);
40481            } else if self.match_token(TokenType::Asc) {
40482                display_name = format!("{} ASC", display_name);
40483            }
40484
40485            identifiers.push(Identifier {
40486                name: display_name,
40487                quoted,
40488                trailing_comments,
40489                span: None,
40490            });
40491
40492            if !self.match_token(TokenType::Comma) {
40493                break;
40494            }
40495        }
40496
40497        Ok(identifiers)
40498    }
40499    // =============================================================================
40500    // Auto-generated Missing Parser Methods
40501    // Total: 296 methods
40502    // =============================================================================
40503
40504    /// parse_add_column - Implemented from Python _parse_add_column
40505    /// Calls: parse_column, parse_column_def_with_exists
40506    #[allow(unused_variables, unused_mut)]
40507    pub fn parse_add_column(&mut self) -> Result<Option<Expression>> {
40508        if self.match_texts(&["FIRST", "AFTER"]) {
40509            // Matched one of: FIRST, AFTER
40510            return Ok(None);
40511        }
40512        Ok(None)
40513    }
40514
40515    /// parse_alias - Parses alias for an expression
40516    /// This method parses just the alias part (AS name or just name)
40517    /// Python: _parse_alias
40518    pub fn parse_alias(&mut self) -> Result<Option<Expression>> {
40519        // Check for AS keyword (explicit alias)
40520        let _explicit = self.match_token(TokenType::Alias);
40521
40522        // Parse the alias identifier
40523        if let Some(alias_expr) = self.parse_id_var()? {
40524            let alias_ident = match alias_expr {
40525                Expression::Identifier(id) => id,
40526                _ => return Ok(None),
40527            };
40528            // Return just the alias identifier wrapped in an expression
40529            return Ok(Some(Expression::Identifier(alias_ident)));
40530        }
40531
40532        Ok(None)
40533    }
40534
40535    /// parse_alias_with_expr - Wraps an expression with an alias if present
40536    pub fn parse_alias_with_expr(
40537        &mut self,
40538        this: Option<Expression>,
40539    ) -> Result<Option<Expression>> {
40540        if this.is_none() {
40541            return Ok(None);
40542        }
40543        let expr = this.unwrap();
40544
40545        // Check for AS keyword (explicit alias)
40546        // Accept both TokenType::Alias and TokenType::As
40547        let has_as = self.match_token(TokenType::Alias) || self.match_token(TokenType::As);
40548
40549        // Check for column aliases: (col1, col2)
40550        if has_as && self.match_token(TokenType::LParen) {
40551            let mut column_aliases = Vec::new();
40552            loop {
40553                if let Some(col_expr) = self.parse_id_var()? {
40554                    if let Expression::Identifier(id) = col_expr {
40555                        column_aliases.push(id);
40556                    }
40557                } else {
40558                    break;
40559                }
40560                if !self.match_token(TokenType::Comma) {
40561                    break;
40562                }
40563            }
40564            self.match_token(TokenType::RParen);
40565
40566            if !column_aliases.is_empty() {
40567                return Ok(Some(Expression::Alias(Box::new(Alias {
40568                    this: expr,
40569                    alias: Identifier::new(String::new()), // Empty alias when only column aliases
40570                    column_aliases,
40571                    pre_alias_comments: Vec::new(),
40572                    trailing_comments: Vec::new(),
40573                    inferred_type: None,
40574                }))));
40575            }
40576        }
40577
40578        // Parse the alias identifier
40579        if let Some(alias_expr) = self.parse_id_var()? {
40580            let alias_ident = match alias_expr {
40581                Expression::Identifier(id) => id,
40582                _ => return Ok(Some(expr)),
40583            };
40584            return Ok(Some(Expression::Alias(Box::new(Alias {
40585                this: expr,
40586                alias: alias_ident,
40587                column_aliases: Vec::new(),
40588                pre_alias_comments: Vec::new(),
40589                trailing_comments: Vec::new(),
40590                inferred_type: None,
40591            }))));
40592        }
40593
40594        Ok(Some(expr))
40595    }
40596
40597    /// parse_alter_diststyle - Implemented from Python _parse_alter_diststyle
40598    #[allow(unused_variables, unused_mut)]
40599    /// parse_alter_diststyle - Parses ALTER TABLE DISTSTYLE clause (Redshift)
40600    /// Python: parser.py:7797-7802
40601    pub fn parse_alter_diststyle(&mut self) -> Result<Option<Expression>> {
40602        // Check for ALL, EVEN, AUTO
40603        if self.match_texts(&["ALL", "EVEN", "AUTO"]) {
40604            let style = self.previous().text.to_ascii_uppercase();
40605            return Ok(Some(Expression::DistStyleProperty(Box::new(
40606                DistStyleProperty {
40607                    this: Box::new(Expression::Identifier(Identifier::new(style))),
40608                },
40609            ))));
40610        }
40611
40612        // KEY DISTKEY column
40613        if self.match_text_seq(&["KEY", "DISTKEY"]) {
40614            if let Some(column) = self.parse_column()? {
40615                return Ok(Some(Expression::DistStyleProperty(Box::new(
40616                    DistStyleProperty {
40617                        this: Box::new(column),
40618                    },
40619                ))));
40620            }
40621        }
40622
40623        Ok(None)
40624    }
40625
40626    /// parse_alter_session - Parses ALTER SESSION SET/UNSET statements
40627    /// Python: parser.py:7879-7889
40628    pub fn parse_alter_session(&mut self) -> Result<Option<Expression>> {
40629        // ALTER SESSION SET var = value, ...
40630        if self.match_token(TokenType::Set) {
40631            let mut expressions = Vec::new();
40632            loop {
40633                if let Some(item) = self.parse_set_item_assignment()? {
40634                    expressions.push(item);
40635                }
40636                if !self.match_token(TokenType::Comma) {
40637                    break;
40638                }
40639            }
40640            return Ok(Some(Expression::AlterSession(Box::new(AlterSession {
40641                expressions,
40642                unset: None,
40643            }))));
40644        }
40645
40646        // ALTER SESSION UNSET var, ...
40647        if self.match_text_seq(&["UNSET"]) {
40648            let mut expressions = Vec::new();
40649            loop {
40650                if let Some(var) = self.parse_id_var()? {
40651                    // For UNSET, we just use the identifier directly
40652                    expressions.push(var);
40653                }
40654                if !self.match_token(TokenType::Comma) {
40655                    break;
40656                }
40657            }
40658            return Ok(Some(Expression::AlterSession(Box::new(AlterSession {
40659                expressions,
40660                unset: Some(Box::new(Expression::Boolean(BooleanLiteral {
40661                    value: true,
40662                }))),
40663            }))));
40664        }
40665
40666        Ok(None)
40667    }
40668
40669    /// parse_alter_sortkey - Parses ALTER TABLE SORTKEY clause (Redshift)
40670    /// Python: parser.py:7804-7816
40671    pub fn parse_alter_sortkey(&mut self) -> Result<Option<Expression>> {
40672        self.parse_alter_sortkey_impl(None)
40673    }
40674
40675    /// Implementation of parse_alter_sortkey with compound option
40676    pub fn parse_alter_sortkey_impl(
40677        &mut self,
40678        compound: Option<bool>,
40679    ) -> Result<Option<Expression>> {
40680        // For compound sortkey, match SORTKEY keyword
40681        if compound == Some(true) {
40682            self.match_text_seq(&["SORTKEY"]);
40683        }
40684
40685        // Check for (column_list) syntax
40686        if self.check(TokenType::LParen) {
40687            let wrapped = self.parse_wrapped_id_vars()?;
40688            // Extract expressions from Tuple
40689            let expressions = if let Some(Expression::Tuple(t)) = wrapped {
40690                t.expressions
40691            } else {
40692                Vec::new()
40693            };
40694            return Ok(Some(Expression::AlterSortKey(Box::new(AlterSortKey {
40695                this: None,
40696                expressions,
40697                compound: compound
40698                    .map(|c| Box::new(Expression::Boolean(BooleanLiteral { value: c }))),
40699            }))));
40700        }
40701
40702        // Check for AUTO or NONE
40703        if self.match_texts(&["AUTO", "NONE"]) {
40704            let style = self.previous().text.to_ascii_uppercase();
40705            return Ok(Some(Expression::AlterSortKey(Box::new(AlterSortKey {
40706                this: Some(Box::new(Expression::Identifier(Identifier::new(style)))),
40707                expressions: Vec::new(),
40708                compound: compound
40709                    .map(|c| Box::new(Expression::Boolean(BooleanLiteral { value: c }))),
40710            }))));
40711        }
40712
40713        Ok(None)
40714    }
40715
40716    /// parse_alter_table_add - Parses ALTER TABLE ADD clause
40717    /// Python: parser.py:7715-7751
40718    pub fn parse_alter_table_add(&mut self) -> Result<Option<Expression>> {
40719        // Check for ADD keyword (optional in some contexts)
40720        self.match_text_seq(&["ADD"]);
40721
40722        // Check for INDEX/KEY with optional FULLTEXT/SPATIAL prefix (MySQL)
40723        // Syntax: ADD [FULLTEXT|SPATIAL] {INDEX|KEY} [name] (columns) [USING {BTREE|HASH}]
40724        let kind = if self.match_identifier("FULLTEXT") {
40725            Some("FULLTEXT".to_string())
40726        } else if self.match_identifier("SPATIAL") {
40727            Some("SPATIAL".to_string())
40728        } else {
40729            None
40730        };
40731
40732        if self.check(TokenType::Index) || self.check(TokenType::Key) || kind.is_some() {
40733            // Consume INDEX or KEY keyword, track which was used
40734            let use_key_keyword = if self.match_token(TokenType::Key) {
40735                true
40736            } else {
40737                self.match_token(TokenType::Index);
40738                false
40739            };
40740
40741            // Optional index name (before the columns)
40742            let name = if !self.check(TokenType::LParen) && !self.check(TokenType::Using) {
40743                Some(self.expect_identifier_with_quoted()?)
40744            } else {
40745                None
40746            };
40747
40748            // Parse columns (with optional prefix length and DESC)
40749            self.expect(TokenType::LParen)?;
40750            let columns = self.parse_index_identifier_list()?;
40751            self.expect(TokenType::RParen)?;
40752
40753            // Parse optional USING BTREE|HASH
40754            let modifiers = self.parse_constraint_modifiers();
40755
40756            return Ok(Some(Expression::AlterTable(Box::new(AlterTable {
40757                name: TableRef::new(""),
40758                actions: vec![AlterTableAction::AddConstraint(TableConstraint::Index {
40759                    name,
40760                    columns,
40761                    kind,
40762                    modifiers,
40763                    use_key_keyword,
40764                    expression: None,
40765                    index_type: None,
40766                    granularity: None,
40767                })],
40768                if_exists: false,
40769                algorithm: None,
40770                lock: None,
40771                with_check: None,
40772                partition: None,
40773                on_cluster: None,
40774            }))));
40775        }
40776
40777        // Check for constraint keywords (PRIMARY KEY, FOREIGN KEY, UNIQUE, CHECK, CONSTRAINT)
40778        if self.check(TokenType::PrimaryKey)
40779            || self.check(TokenType::ForeignKey)
40780            || self.check(TokenType::Unique)
40781            || self.check(TokenType::Check)
40782            || self.check(TokenType::Constraint)
40783        {
40784            // Parse a single constraint and return it wrapped in Constraint
40785            if let Some(constraint) = self.parse_constraint()? {
40786                return Ok(Some(Expression::Constraint(Box::new(Constraint {
40787                    this: Box::new(constraint),
40788                    expressions: Vec::new(),
40789                }))));
40790            }
40791        }
40792
40793        // Check for COLUMNS keyword (batch column addition)
40794        if self.match_text_seq(&["COLUMNS"]) {
40795            // Parse schema or column definitions
40796            if let Some(schema) = self.parse_schema()? {
40797                return Ok(Some(schema));
40798            }
40799        }
40800
40801        // Check for IF NOT EXISTS PARTITION (must check before parse_add_column)
40802        let exists = self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists]);
40803        if self.match_token(TokenType::Partition) {
40804            // Parse PARTITION(key = value, ...)
40805            self.expect(TokenType::LParen)?;
40806            let mut partition_exprs = Vec::new();
40807            loop {
40808                if let Some(expr) = self.parse_conjunction()? {
40809                    partition_exprs.push(expr);
40810                }
40811                if !self.match_token(TokenType::Comma) {
40812                    break;
40813                }
40814            }
40815            self.expect(TokenType::RParen)?;
40816
40817            let partition = Expression::Partition(Box::new(crate::expressions::Partition {
40818                expressions: partition_exprs,
40819                subpartition: false,
40820            }));
40821
40822            let location = if self.match_text_seq(&["LOCATION"]) {
40823                self.parse_property()?
40824            } else {
40825                None
40826            };
40827            return Ok(Some(Expression::AddPartition(Box::new(AddPartition {
40828                this: Box::new(partition),
40829                exists,
40830                location: location.map(Box::new),
40831            }))));
40832        }
40833
40834        // Try to parse column definition (after checking for PARTITION)
40835        if let Some(column) = self.parse_add_column()? {
40836            return Ok(Some(column));
40837        }
40838
40839        Ok(None)
40840    }
40841
40842    /// parse_alter_table_alter - Parses ALTER TABLE ALTER COLUMN clause
40843    /// Python: parser.py:7753-7795
40844    pub fn parse_alter_table_alter(&mut self) -> Result<Option<Expression>> {
40845        // Match optional COLUMN keyword
40846        self.match_token(TokenType::Column);
40847
40848        // Parse the column name - required for ALTER COLUMN
40849        let column = match self.parse_field()? {
40850            Some(c) => c,
40851            None => return Ok(None),
40852        };
40853
40854        // DROP DEFAULT
40855        if self.match_keywords(&[TokenType::Drop, TokenType::Default]) {
40856            return Ok(Some(Expression::AlterColumn(Box::new(AlterColumn {
40857                this: Box::new(column),
40858                dtype: None,
40859                collate: None,
40860                using: None,
40861                default: None,
40862                drop: Some(Box::new(Expression::Boolean(BooleanLiteral {
40863                    value: true,
40864                }))),
40865                allow_null: None,
40866                comment: None,
40867                visible: None,
40868                rename_to: None,
40869            }))));
40870        }
40871
40872        // SET DEFAULT expr
40873        if self.match_keywords(&[TokenType::Set, TokenType::Default]) {
40874            let default_val = self.parse_disjunction()?;
40875            return Ok(Some(Expression::AlterColumn(Box::new(AlterColumn {
40876                this: Box::new(column),
40877                dtype: None,
40878                collate: None,
40879                using: None,
40880                default: default_val.map(Box::new),
40881                drop: None,
40882                allow_null: None,
40883                comment: None,
40884                visible: None,
40885                rename_to: None,
40886            }))));
40887        }
40888
40889        // COMMENT 'string'
40890        if self.match_token(TokenType::Comment) {
40891            let comment_val = self.parse_string()?;
40892            return Ok(Some(Expression::AlterColumn(Box::new(AlterColumn {
40893                this: Box::new(column),
40894                dtype: None,
40895                collate: None,
40896                using: None,
40897                default: None,
40898                drop: None,
40899                allow_null: None,
40900                comment: comment_val.map(Box::new),
40901                visible: None,
40902                rename_to: None,
40903            }))));
40904        }
40905
40906        // DROP NOT NULL
40907        if self.match_text_seq(&["DROP", "NOT", "NULL"]) {
40908            return Ok(Some(Expression::AlterColumn(Box::new(AlterColumn {
40909                this: Box::new(column),
40910                dtype: None,
40911                collate: None,
40912                using: None,
40913                default: None,
40914                drop: Some(Box::new(Expression::Boolean(BooleanLiteral {
40915                    value: true,
40916                }))),
40917                allow_null: Some(Box::new(Expression::Boolean(BooleanLiteral {
40918                    value: true,
40919                }))),
40920                comment: None,
40921                visible: None,
40922                rename_to: None,
40923            }))));
40924        }
40925
40926        // SET NOT NULL
40927        if self.match_text_seq(&["SET", "NOT", "NULL"]) {
40928            return Ok(Some(Expression::AlterColumn(Box::new(AlterColumn {
40929                this: Box::new(column),
40930                dtype: None,
40931                collate: None,
40932                using: None,
40933                default: None,
40934                drop: None,
40935                allow_null: Some(Box::new(Expression::Boolean(BooleanLiteral {
40936                    value: false,
40937                }))),
40938                comment: None,
40939                visible: None,
40940                rename_to: None,
40941            }))));
40942        }
40943
40944        // SET VISIBLE
40945        if self.match_text_seq(&["SET", "VISIBLE"]) {
40946            return Ok(Some(Expression::AlterColumn(Box::new(AlterColumn {
40947                this: Box::new(column),
40948                dtype: None,
40949                collate: None,
40950                using: None,
40951                default: None,
40952                drop: None,
40953                allow_null: None,
40954                comment: None,
40955                visible: Some(Box::new(Expression::Identifier(Identifier::new(
40956                    "VISIBLE".to_string(),
40957                )))),
40958                rename_to: None,
40959            }))));
40960        }
40961
40962        // SET INVISIBLE
40963        if self.match_text_seq(&["SET", "INVISIBLE"]) {
40964            return Ok(Some(Expression::AlterColumn(Box::new(AlterColumn {
40965                this: Box::new(column),
40966                dtype: None,
40967                collate: None,
40968                using: None,
40969                default: None,
40970                drop: None,
40971                allow_null: None,
40972                comment: None,
40973                visible: Some(Box::new(Expression::Identifier(Identifier::new(
40974                    "INVISIBLE".to_string(),
40975                )))),
40976                rename_to: None,
40977            }))));
40978        }
40979
40980        // [SET DATA] TYPE type [COLLATE collation] [USING expr]
40981        self.match_text_seq(&["SET", "DATA"]);
40982        self.match_text_seq(&["TYPE"]);
40983
40984        let dtype = self.parse_types()?;
40985        let collate = if self.match_token(TokenType::Collate) {
40986            self.parse_term()?
40987        } else {
40988            None
40989        };
40990        let using = if self.match_token(TokenType::Using) {
40991            self.parse_disjunction()?
40992        } else {
40993            None
40994        };
40995
40996        Ok(Some(Expression::AlterColumn(Box::new(AlterColumn {
40997            this: Box::new(column),
40998            dtype: dtype.map(Box::new),
40999            collate: collate.map(Box::new),
41000            using: using.map(Box::new),
41001            default: None,
41002            drop: None,
41003            allow_null: None,
41004            comment: None,
41005            visible: None,
41006            rename_to: None,
41007        }))))
41008    }
41009
41010    /// Parse ALTER TABLE DROP action
41011    /// Note: Main ALTER TABLE DROP logic is implemented inline in parse_alter_table
41012    /// This method provides a separate entry point for the same functionality
41013    pub fn parse_alter_table_drop(&mut self) -> Result<Option<Expression>> {
41014        // Check for IF EXISTS before PARTITION
41015        let exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
41016
41017        // Check if this is DROP PARTITION
41018        if self.check(TokenType::Partition) {
41019            return self.parse_drop_partition_with_exists(exists);
41020        }
41021
41022        // Check for DROP FOREIGN KEY (Oracle/MySQL)
41023        if self.match_keywords(&[TokenType::ForeignKey, TokenType::Key]) {
41024            let name = self.expect_identifier_with_quoted()?;
41025            return Ok(Some(Expression::AlterTable(Box::new(AlterTable {
41026                name: TableRef::new(""),
41027                actions: vec![AlterTableAction::DropForeignKey { name }],
41028                if_exists: false,
41029                algorithm: None,
41030                lock: None,
41031                with_check: None,
41032                partition: None,
41033                on_cluster: None,
41034            }))));
41035        }
41036
41037        // Check for DROP COLUMNS (col1, col2, ...) syntax (Spark/Databricks)
41038        if self.check_identifier("COLUMNS") && self.check_next(TokenType::LParen) {
41039            self.skip(); // consume COLUMNS
41040            self.expect(TokenType::LParen)?;
41041            let mut columns = Vec::new();
41042            loop {
41043                if let Some(col) = self.parse_identifier()? {
41044                    columns.push(col);
41045                }
41046                if !self.match_token(TokenType::Comma) {
41047                    break;
41048                }
41049            }
41050            self.expect(TokenType::RParen)?;
41051            if columns.is_empty() {
41052                return Ok(None);
41053            } else if columns.len() == 1 {
41054                return Ok(Some(columns.remove(0)));
41055            } else {
41056                return Ok(Some(Expression::Tuple(Box::new(Tuple {
41057                    expressions: columns,
41058                }))));
41059            }
41060        }
41061
41062        // Otherwise, parse as DROP COLUMN(s)
41063        let mut columns = Vec::new();
41064
41065        // Parse first column
41066        if let Some(col) = self.parse_drop_column()? {
41067            columns.push(col);
41068        }
41069
41070        // Parse additional columns (comma-separated)
41071        while self.match_token(TokenType::Comma) {
41072            // Match optional DROP keyword before next column
41073            self.match_token(TokenType::Drop);
41074            if let Some(col) = self.parse_drop_column()? {
41075                columns.push(col);
41076            }
41077        }
41078
41079        if columns.is_empty() {
41080            Ok(None)
41081        } else if columns.len() == 1 {
41082            Ok(Some(columns.remove(0)))
41083        } else {
41084            // Multiple columns - wrap in a Tuple
41085            Ok(Some(Expression::Tuple(Box::new(Tuple {
41086                expressions: columns,
41087            }))))
41088        }
41089    }
41090
41091    /// parse_alter_table_rename - Parses ALTER TABLE RENAME clause
41092    /// Python: parser.py:7828-7841
41093    pub fn parse_alter_table_rename(&mut self) -> Result<Option<Expression>> {
41094        // RENAME COLUMN old_name TO new_name
41095        if self.match_token(TokenType::Column) {
41096            let exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
41097            let old_column = match self.parse_column()? {
41098                Some(c) => c,
41099                None => return Ok(None),
41100            };
41101
41102            if !self.match_text_seq(&["TO"]) {
41103                return Ok(None);
41104            }
41105
41106            let new_column = self.parse_column()?;
41107
41108            return Ok(Some(Expression::RenameColumn(Box::new(RenameColumn {
41109                this: Box::new(old_column),
41110                to: new_column.map(Box::new),
41111                exists,
41112            }))));
41113        }
41114
41115        // RENAME TO new_table_name
41116        if self.match_text_seq(&["TO"]) {
41117            // Return the table expression directly - the caller will handle it as a rename target
41118            let new_table = self.parse_table()?;
41119            return Ok(new_table);
41120        }
41121
41122        // SQLite allows: RENAME old_name TO new_name (without COLUMN keyword)
41123        // Try to parse as column rename if followed by identifier and TO
41124        if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
41125            let old_column = match self.parse_column()? {
41126                Some(c) => c,
41127                None => return Ok(None),
41128            };
41129
41130            if self.match_text_seq(&["TO"]) {
41131                let new_column = self.parse_column()?;
41132                return Ok(Some(Expression::RenameColumn(Box::new(RenameColumn {
41133                    this: Box::new(old_column),
41134                    to: new_column.map(Box::new),
41135                    exists: false,
41136                }))));
41137            } else {
41138                // Not TO after identifier - put it back and return error
41139                return Err(self.parse_error("Expected COLUMN or TO after RENAME"));
41140            }
41141        }
41142
41143        Ok(None)
41144    }
41145
41146    /// parse_alter_table_set - Parses ALTER TABLE SET clause
41147    /// Python: parser.py:7843-7877
41148    pub fn parse_alter_table_set(&mut self) -> Result<Option<Expression>> {
41149        let mut alter_set = AlterSet {
41150            expressions: Vec::new(),
41151            option: None,
41152            tablespace: None,
41153            access_method: None,
41154            file_format: None,
41155            copy_options: None,
41156            tag: None,
41157            location: None,
41158            serde: None,
41159        };
41160
41161        // SET AUTHORIZATION [ROLE] user
41162        if self.match_token(TokenType::Authorization) {
41163            let mut auth_text = "AUTHORIZATION ".to_string();
41164            if self.match_texts(&["ROLE"]) {
41165                auth_text.push_str("ROLE ");
41166            }
41167            let user = self.expect_identifier()?;
41168            auth_text.push_str(&user);
41169            alter_set.option = Some(Box::new(Expression::Identifier(Identifier::new(auth_text))));
41170            return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
41171        }
41172
41173        // SET PROPERTIES prop = value, ...
41174        if self.match_text_seq(&["PROPERTIES"]) {
41175            let mut assignments = Vec::new();
41176            loop {
41177                // Parse property name (could be identifier or string literal)
41178                let key = if self.check(TokenType::String) {
41179                    self.parse_string()?.unwrap_or(Expression::Null(Null))
41180                } else {
41181                    let name = self.expect_identifier()?;
41182                    Expression::Identifier(Identifier::new(name))
41183                };
41184                self.expect(TokenType::Eq)?;
41185                // Parse value (could be DEFAULT or an expression)
41186                let value = if self.match_token(TokenType::Default) {
41187                    Expression::Identifier(Identifier::new("DEFAULT".to_string()))
41188                } else {
41189                    self.parse_expression()?
41190                };
41191                assignments.push(Expression::Eq(Box::new(BinaryOp {
41192                    left: key,
41193                    right: value,
41194                    left_comments: Vec::new(),
41195                    operator_comments: Vec::new(),
41196                    trailing_comments: Vec::new(),
41197                    inferred_type: None,
41198                })));
41199                if !self.match_token(TokenType::Comma) {
41200                    break;
41201                }
41202            }
41203            alter_set.expressions = assignments;
41204            return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
41205        }
41206
41207        // SET (properties) or SET TABLE PROPERTIES (properties)
41208        if self.check(TokenType::LParen) || self.match_text_seq(&["TABLE", "PROPERTIES"]) {
41209            let assignments = self.parse_wrapped_csv_assignments()?;
41210            alter_set.expressions = assignments;
41211            return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
41212        }
41213
41214        // SET FILESTREAM_ON = value
41215        if self.match_text_seq(&["FILESTREAM_ON"]) {
41216            if let Some(assignment) = self.parse_assignment()? {
41217                alter_set.expressions = vec![assignment];
41218            }
41219            return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
41220        }
41221
41222        // SET LOGGED or SET UNLOGGED
41223        if self.match_texts(&["LOGGED", "UNLOGGED"]) {
41224            let option = self.previous().text.to_ascii_uppercase();
41225            alter_set.option = Some(Box::new(Expression::Identifier(Identifier::new(option))));
41226            return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
41227        }
41228
41229        // SET WITHOUT CLUSTER or SET WITHOUT OIDS
41230        if self.match_text_seq(&["WITHOUT"]) {
41231            if self.match_texts(&["CLUSTER", "OIDS"]) {
41232                let option = format!("WITHOUT {}", self.previous().text.to_ascii_uppercase());
41233                alter_set.option = Some(Box::new(Expression::Identifier(Identifier::new(option))));
41234                return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
41235            }
41236        }
41237
41238        // SET LOCATION path
41239        if self.match_text_seq(&["LOCATION"]) {
41240            let loc = self.parse_field()?;
41241            alter_set.location = loc.map(Box::new);
41242            return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
41243        }
41244
41245        // SET ACCESS METHOD method
41246        if self.match_text_seq(&["ACCESS", "METHOD"]) {
41247            let method = self.parse_field()?;
41248            alter_set.access_method = method.map(Box::new);
41249            return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
41250        }
41251
41252        // SET TABLESPACE name
41253        if self.match_text_seq(&["TABLESPACE"]) {
41254            let tablespace = self.parse_field()?;
41255            alter_set.tablespace = tablespace.map(Box::new);
41256            return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
41257        }
41258
41259        // SET FILE FORMAT format or SET FILEFORMAT format
41260        if self.match_text_seq(&["FILE", "FORMAT"]) || self.match_text_seq(&["FILEFORMAT"]) {
41261            let format = self.parse_field()?;
41262            alter_set.file_format = format.map(Box::new);
41263            return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
41264        }
41265
41266        // SET STAGE_FILE_FORMAT = (options)
41267        if self.match_text_seq(&["STAGE_FILE_FORMAT"]) {
41268            let options = self.parse_wrapped_options()?;
41269            alter_set.file_format = options.map(Box::new);
41270            return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
41271        }
41272
41273        // SET STAGE_COPY_OPTIONS = (options)
41274        if self.match_text_seq(&["STAGE_COPY_OPTIONS"]) {
41275            let options = self.parse_wrapped_options()?;
41276            alter_set.copy_options = options.map(Box::new);
41277            return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
41278        }
41279
41280        // SET TAG or SET TAGS
41281        if self.match_text_seq(&["TAG"]) || self.match_text_seq(&["TAGS"]) {
41282            let mut tags = Vec::new();
41283            loop {
41284                if let Some(assignment) = self.parse_assignment()? {
41285                    tags.push(assignment);
41286                }
41287                if !self.match_token(TokenType::Comma) {
41288                    break;
41289                }
41290            }
41291            if !tags.is_empty() {
41292                alter_set.tag = Some(Box::new(Expression::Tuple(Box::new(Tuple {
41293                    expressions: tags,
41294                }))));
41295            }
41296            return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
41297        }
41298
41299        // SET SERDE 'class' [WITH SERDEPROPERTIES (...)]
41300        if self.match_text_seq(&["SERDE"]) {
41301            let serde = self.parse_field()?;
41302            alter_set.serde = serde.map(Box::new);
41303
41304            // Parse optional properties
41305            let properties = self.parse_wrapped()?;
41306            if let Some(props) = properties {
41307                alter_set.expressions = vec![props];
41308            }
41309            return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
41310        }
41311
41312        Ok(None)
41313    }
41314
41315    /// Helper to parse wrapped CSV of assignments
41316    fn parse_wrapped_csv_assignments(&mut self) -> Result<Vec<Expression>> {
41317        if !self.match_token(TokenType::LParen) {
41318            return Ok(Vec::new());
41319        }
41320        let mut assignments = Vec::new();
41321        loop {
41322            if let Some(assignment) = self.parse_assignment()? {
41323                assignments.push(assignment);
41324            }
41325            if !self.match_token(TokenType::Comma) {
41326                break;
41327            }
41328        }
41329        self.expect(TokenType::RParen)?;
41330        Ok(assignments)
41331    }
41332
41333    /// parse_analyze - Implemented from Python _parse_analyze
41334    /// Calls: parse_table_parts, parse_number, parse_table
41335    #[allow(unused_variables, unused_mut)]
41336    /// parse_analyze - Parses ANALYZE statement
41337    /// Python: parser.py:7937-7999
41338    pub fn parse_analyze(&mut self) -> Result<Option<Expression>> {
41339        // If no more tokens, return empty Analyze
41340        if self.is_at_end() {
41341            return Ok(Some(Expression::Analyze(Box::new(Analyze {
41342                kind: None,
41343                this: None,
41344                options: Vec::new(),
41345                mode: None,
41346                partition: None,
41347                expression: None,
41348                properties: Vec::new(),
41349                columns: Vec::new(),
41350            }))));
41351        }
41352
41353        // Parse options (VERBOSE, SKIP_LOCKED, etc.)
41354        // StarRocks uses FULL and SAMPLE as options
41355        let mut options = Vec::new();
41356        let analyze_styles = [
41357            "VERBOSE",
41358            "SKIP_LOCKED",
41359            "BUFFER_USAGE_LIMIT",
41360            "FULL",
41361            "SAMPLE",
41362        ];
41363        while self.match_texts(&analyze_styles) {
41364            let style = self.previous().text.to_ascii_uppercase();
41365            if style == "BUFFER_USAGE_LIMIT" {
41366                // Parse number after BUFFER_USAGE_LIMIT
41367                if let Some(num) = self.parse_number()? {
41368                    options.push(Expression::Identifier(Identifier::new(format!(
41369                        "BUFFER_USAGE_LIMIT {}",
41370                        if let Expression::Literal(Literal::Number(n)) = &num {
41371                            n.clone()
41372                        } else {
41373                            String::new()
41374                        }
41375                    ))));
41376                }
41377            } else {
41378                options.push(Expression::Identifier(Identifier::new(style)));
41379            }
41380        }
41381
41382        let mut this: Option<Expression> = None;
41383        let mut kind: Option<String> = None;
41384        let mut inner_expression: Option<Expression> = None;
41385
41386        // Parse TABLE or INDEX
41387        if self.match_token(TokenType::Table) {
41388            kind = Some("TABLE".to_string());
41389            this = self.parse_table_parts()?;
41390        } else if self.match_token(TokenType::Index) {
41391            kind = Some("INDEX".to_string());
41392            this = self.parse_table_parts()?;
41393        } else if self.match_text_seq(&["TABLES"]) {
41394            kind = Some("TABLES".to_string());
41395            if self.match_token(TokenType::From) || self.match_token(TokenType::In) {
41396                let dir = self.previous().text.to_ascii_uppercase();
41397                kind = Some(format!("TABLES {}", dir));
41398                // Parse database name as identifier
41399                let db_name = self.expect_identifier()?;
41400                this = Some(Expression::Identifier(Identifier::new(db_name)));
41401            }
41402        } else if self.match_text_seq(&["DATABASE"]) {
41403            kind = Some("DATABASE".to_string());
41404            this = self.parse_table_parts()?;
41405        } else if self.match_text_seq(&["CLUSTER"]) {
41406            kind = Some("CLUSTER".to_string());
41407            this = self.parse_table_parts()?;
41408        } else if self.match_texts(&["LOCAL", "NO_WRITE_TO_BINLOG"]) {
41409            // MySQL: ANALYZE LOCAL TABLE tbl / ANALYZE NO_WRITE_TO_BINLOG TABLE tbl
41410            let opt_text = self.previous().text.to_ascii_uppercase();
41411            options.push(Expression::Identifier(Identifier::new(opt_text)));
41412            if self.match_token(TokenType::Table) {
41413                kind = Some("TABLE".to_string());
41414            }
41415            this = self.parse_table_parts()?;
41416        } else if self.match_text_seq(&["COMPUTE"]) {
41417            // Check ANALYZE_EXPRESSION_PARSERS keywords before fallback to parse_table_parts
41418            // Python: elif self._match_texts(self.ANALYZE_EXPRESSION_PARSERS)
41419            inner_expression = self.parse_analyze_statistics()?;
41420        } else if self.match_text_seq(&["DELETE"]) {
41421            inner_expression = self.parse_analyze_delete()?;
41422        } else if self.match_text_seq(&["VALIDATE"]) {
41423            inner_expression = self.parse_analyze_validate()?;
41424        } else if self.match_text_seq(&["LIST"]) {
41425            inner_expression = self.parse_analyze_list()?;
41426        } else if self.match_text_seq(&["DROP"]) {
41427            inner_expression = self.parse_analyze_histogram()?;
41428        } else if self.match_text_seq(&["UPDATE"]) {
41429            inner_expression = self.parse_analyze_histogram()?;
41430        } else if self.match_texts(&["ALL", "PREDICATE"]) {
41431            inner_expression = self.parse_analyze_columns()?;
41432        } else {
41433            // Try to parse table directly (empty kind - https://prestodb.io/docs/current/sql/analyze.html)
41434            this = self.parse_table_parts()?;
41435        }
41436
41437        // Parse optional column list: ANALYZE tbl(col1, col2) (PostgreSQL)
41438        let columns = if this.is_some() && self.match_token(TokenType::LParen) {
41439            let mut cols = Vec::new();
41440            loop {
41441                cols.push(self.expect_identifier_or_keyword()?);
41442                if !self.match_token(TokenType::Comma) {
41443                    break;
41444                }
41445            }
41446            self.expect(TokenType::RParen)?;
41447            cols
41448        } else {
41449            Vec::new()
41450        };
41451
41452        // Parse optional PARTITION
41453        let partition = self.parse_partition()?;
41454
41455        // Parse optional WITH SYNC/ASYNC MODE or WITH (prop=val, ...) for Presto
41456        let mut mode = None;
41457        let mut properties = Vec::new();
41458
41459        if self.match_text_seq(&["WITH", "SYNC", "MODE"]) {
41460            mode = Some(Box::new(Expression::Identifier(Identifier::new(
41461                "WITH SYNC MODE".to_string(),
41462            ))));
41463        } else if self.match_text_seq(&["WITH", "ASYNC", "MODE"]) {
41464            mode = Some(Box::new(Expression::Identifier(Identifier::new(
41465                "WITH ASYNC MODE".to_string(),
41466            ))));
41467        } else if self.match_text_seq(&["WITH"]) {
41468            // Presto syntax: ANALYZE tbl WITH (prop1=val1, prop2=val2)
41469            if self.match_token(TokenType::LParen) {
41470                loop {
41471                    // Parse key=value pairs
41472                    let key = self.parse_id_var()?;
41473                    if key.is_none() {
41474                        break;
41475                    }
41476
41477                    // Expect = sign
41478                    if self.match_token(TokenType::Eq) {
41479                        // Parse the value
41480                        let value = self.parse_primary()?;
41481                        if let Some(k) = key {
41482                            properties.push(Expression::Property(Box::new(Property {
41483                                this: Box::new(k),
41484                                value: Some(Box::new(value)),
41485                            })));
41486                        }
41487                    } else if let Some(k) = key {
41488                        // Key without value
41489                        properties.push(Expression::Property(Box::new(Property {
41490                            this: Box::new(k),
41491                            value: None,
41492                        })));
41493                    }
41494
41495                    if !self.match_token(TokenType::Comma) {
41496                        break;
41497                    }
41498                }
41499                self.expect(TokenType::RParen)?;
41500            }
41501        }
41502
41503        // Parse optional inner expressions (COMPUTE, DELETE, etc.)
41504        // Only if inner_expression wasn't already set (for cases like ANALYZE TABLE tbl VALIDATE...)
41505        if inner_expression.is_none() {
41506            if self.match_text_seq(&["COMPUTE"]) {
41507                inner_expression = self.parse_analyze_statistics()?;
41508            } else if self.match_text_seq(&["DELETE"]) {
41509                inner_expression = self.parse_analyze_delete()?;
41510            } else if self.match_text_seq(&["VALIDATE"]) {
41511                inner_expression = self.parse_analyze_validate()?;
41512            } else if self.match_text_seq(&["LIST"]) {
41513                inner_expression = self.parse_analyze_list()?;
41514            } else if self.match_text_seq(&["DROP"]) {
41515                inner_expression = self.parse_analyze_histogram()?;
41516            } else if self.match_text_seq(&["UPDATE"]) {
41517                inner_expression = self.parse_analyze_histogram()?;
41518            } else if self.match_texts(&["ALL", "PREDICATE"]) {
41519                // Redshift: ANALYZE TBL ALL COLUMNS / ANALYZE TBL PREDICATE COLUMNS
41520                inner_expression = self.parse_analyze_columns()?;
41521            }
41522        }
41523
41524        // Parse optional properties (if not already parsed from WITH clause)
41525        // StarRocks syntax: ANALYZE TABLE TBL PROPERTIES ('prop1'=val1, 'prop2'=val2)
41526        if properties.is_empty() && self.match_text_seq(&["PROPERTIES"]) {
41527            if self.match_token(TokenType::LParen) {
41528                loop {
41529                    // Parse key (can be a string literal or identifier)
41530                    let key = if self.check(TokenType::String) {
41531                        self.skip();
41532                        let key_str = self.previous().text.clone();
41533                        Expression::Literal(Literal::String(key_str))
41534                    } else {
41535                        self.parse_id_var()?
41536                            .unwrap_or(Expression::Identifier(Identifier::new(String::new())))
41537                    };
41538
41539                    // Expect = sign
41540                    if self.match_token(TokenType::Eq) {
41541                        // Parse the value
41542                        let value = self.parse_primary()?;
41543                        properties.push(Expression::Property(Box::new(Property {
41544                            this: Box::new(key),
41545                            value: Some(Box::new(value)),
41546                        })));
41547                    } else {
41548                        // Key without value
41549                        properties.push(Expression::Property(Box::new(Property {
41550                            this: Box::new(key),
41551                            value: None,
41552                        })));
41553                    }
41554
41555                    if !self.match_token(TokenType::Comma) {
41556                        break;
41557                    }
41558                }
41559                self.expect(TokenType::RParen)?;
41560            }
41561        }
41562
41563        Ok(Some(Expression::Analyze(Box::new(Analyze {
41564            kind,
41565            this: this.map(Box::new),
41566            options,
41567            mode,
41568            partition: partition.map(Box::new),
41569            expression: inner_expression.map(Box::new),
41570            properties,
41571            columns,
41572        }))))
41573    }
41574
41575    /// parse_analyze_columns - Parses ANALYZE ... COLUMNS
41576    /// Python: parser.py:8055-8059
41577    /// Note: AnalyzeColumns not in expressions.rs, using Identifier instead
41578    pub fn parse_analyze_columns(&mut self) -> Result<Option<Expression>> {
41579        let prev_text = self.previous().text.to_ascii_uppercase();
41580        if self.match_text_seq(&["COLUMNS"]) {
41581            return Ok(Some(Expression::Identifier(Identifier::new(format!(
41582                "{} COLUMNS",
41583                prev_text
41584            )))));
41585        }
41586        Ok(None)
41587    }
41588
41589    /// parse_analyze_delete - Parses ANALYZE DELETE STATISTICS
41590    /// Python: parser.py:8061-8065
41591    pub fn parse_analyze_delete(&mut self) -> Result<Option<Expression>> {
41592        let kind = if self.match_text_seq(&["SYSTEM"]) {
41593            Some("SYSTEM".to_string())
41594        } else {
41595            None
41596        };
41597
41598        if self.match_text_seq(&["STATISTICS"]) {
41599            return Ok(Some(Expression::AnalyzeDelete(Box::new(AnalyzeDelete {
41600                kind,
41601            }))));
41602        }
41603
41604        Ok(None)
41605    }
41606
41607    /// parse_analyze_histogram - Parses ANALYZE ... HISTOGRAM ON
41608    /// Python: parser.py:8073-8108
41609    pub fn parse_analyze_histogram(&mut self) -> Result<Option<Expression>> {
41610        let action = self.previous().text.to_ascii_uppercase(); // DROP or UPDATE
41611        let mut expressions = Vec::new();
41612        let mut update_options: Option<Box<Expression>> = None;
41613        let mut expression: Option<Box<Expression>> = None;
41614
41615        if !self.match_text_seq(&["HISTOGRAM", "ON"]) {
41616            return Ok(None);
41617        }
41618
41619        // Parse column references
41620        loop {
41621            if let Some(col) = self.parse_column_reference()? {
41622                expressions.push(col);
41623            } else {
41624                break;
41625            }
41626            if !self.match_token(TokenType::Comma) {
41627                break;
41628            }
41629        }
41630
41631        // Parse USING DATA 'json_data' (MySQL) - must check before WITH
41632        if self.match_text_seq(&["USING", "DATA"]) {
41633            if self.check(TokenType::String) {
41634                let tok = self.advance();
41635                expression = Some(Box::new(Expression::Identifier(Identifier::new(format!(
41636                    "USING DATA '{}'",
41637                    tok.text
41638                )))));
41639            } else {
41640                expression = Some(Box::new(Expression::Identifier(Identifier::new(
41641                    "USING DATA".to_string(),
41642                ))));
41643            }
41644        }
41645
41646        // Parse WITH options - can have two WITH clauses:
41647        // 1. WITH SYNC/ASYNC MODE (optional)
41648        // 2. WITH n BUCKETS (optional)
41649        // StarRocks syntax: WITH SYNC MODE WITH 5 BUCKETS
41650        let mut mode_str: Option<String> = None;
41651        let mut buckets_str: Option<String> = None;
41652
41653        if self.match_token(TokenType::With) {
41654            if self.match_texts(&["SYNC", "ASYNC"]) {
41655                let mode = self.previous().text.to_ascii_uppercase();
41656                if self.match_text_seq(&["MODE"]) {
41657                    mode_str = Some(format!("WITH {} MODE", mode));
41658                }
41659                // Check for second WITH clause for buckets
41660                if self.match_token(TokenType::With) {
41661                    if let Some(num) = self.parse_number()? {
41662                        if self.match_text_seq(&["BUCKETS"]) {
41663                            let num_str = if let Expression::Literal(Literal::Number(n)) = &num {
41664                                n.clone()
41665                            } else {
41666                                String::new()
41667                            };
41668                            buckets_str = Some(format!("WITH {} BUCKETS", num_str));
41669                        }
41670                    }
41671                }
41672            } else if let Some(num) = self.parse_number()? {
41673                if self.match_text_seq(&["BUCKETS"]) {
41674                    let num_str = if let Expression::Literal(Literal::Number(n)) = &num {
41675                        n.clone()
41676                    } else {
41677                        String::new()
41678                    };
41679                    buckets_str = Some(format!("WITH {} BUCKETS", num_str));
41680                }
41681            }
41682        }
41683
41684        // Combine mode and buckets into expression
41685        match (mode_str, buckets_str) {
41686            (Some(m), Some(b)) => {
41687                expression = Some(Box::new(Expression::Identifier(Identifier::new(format!(
41688                    "{} {}",
41689                    m, b
41690                )))));
41691            }
41692            (Some(m), None) => {
41693                expression = Some(Box::new(Expression::Identifier(Identifier::new(m))));
41694            }
41695            (None, Some(b)) => {
41696                expression = Some(Box::new(Expression::Identifier(Identifier::new(b))));
41697            }
41698            (None, None) => {}
41699        }
41700
41701        // Parse AUTO UPDATE or MANUAL UPDATE (MySQL 8.0.27+)
41702        if self.match_texts(&["MANUAL", "AUTO"]) {
41703            let mode = self.previous().text.to_ascii_uppercase();
41704            if self.check(TokenType::Update) {
41705                update_options = Some(Box::new(Expression::Identifier(Identifier::new(mode))));
41706                self.skip(); // consume UPDATE
41707            }
41708        }
41709
41710        Ok(Some(Expression::AnalyzeHistogram(Box::new(
41711            AnalyzeHistogram {
41712                this: Box::new(Expression::Identifier(Identifier::new(action))),
41713                expressions,
41714                expression,
41715                update_options,
41716            },
41717        ))))
41718    }
41719
41720    /// parse_analyze_list - Parses ANALYZE LIST CHAINED ROWS
41721    /// Python: parser.py:8067-8070
41722    pub fn parse_analyze_list(&mut self) -> Result<Option<Expression>> {
41723        if self.match_text_seq(&["CHAINED", "ROWS"]) {
41724            let expression = self.parse_into()?.map(Box::new);
41725            return Ok(Some(Expression::AnalyzeListChainedRows(Box::new(
41726                AnalyzeListChainedRows { expression },
41727            ))));
41728        }
41729        Ok(None)
41730    }
41731
41732    /// parse_analyze_statistics - Parses ANALYZE ... STATISTICS
41733    /// Python: parser.py:8002-8031
41734    pub fn parse_analyze_statistics(&mut self) -> Result<Option<Expression>> {
41735        let kind = self.previous().text.to_ascii_uppercase();
41736        let option = if self.match_text_seq(&["DELTA"]) {
41737            Some(Box::new(Expression::Identifier(Identifier::new(
41738                "DELTA".to_string(),
41739            ))))
41740        } else {
41741            None
41742        };
41743
41744        // Expect STATISTICS keyword
41745        if !self.match_text_seq(&["STATISTICS"]) {
41746            return Ok(None);
41747        }
41748
41749        let mut this: Option<Box<Expression>> = None;
41750        let mut expressions = Vec::new();
41751
41752        if self.match_text_seq(&["NOSCAN"]) {
41753            this = Some(Box::new(Expression::Identifier(Identifier::new(
41754                "NOSCAN".to_string(),
41755            ))));
41756        } else if self.match_token(TokenType::For) {
41757            if self.match_text_seq(&["ALL", "COLUMNS"]) {
41758                this = Some(Box::new(Expression::Identifier(Identifier::new(
41759                    "FOR ALL COLUMNS".to_string(),
41760                ))));
41761            } else if self.match_text_seq(&["COLUMNS"]) {
41762                this = Some(Box::new(Expression::Identifier(Identifier::new(
41763                    "FOR COLUMNS".to_string(),
41764                ))));
41765                // Parse column list
41766                loop {
41767                    if let Some(col) = self.parse_column_reference()? {
41768                        expressions.push(col);
41769                    } else {
41770                        break;
41771                    }
41772                    if !self.match_token(TokenType::Comma) {
41773                        break;
41774                    }
41775                }
41776            }
41777        } else if self.match_text_seq(&["SAMPLE"]) {
41778            // Parse SAMPLE number [PERCENT]
41779            if let Some(sample) = self.parse_number()? {
41780                let sample_kind = if self.match_token(TokenType::Percent) {
41781                    Some("PERCENT".to_string())
41782                } else {
41783                    None
41784                };
41785                expressions.push(Expression::AnalyzeSample(Box::new(AnalyzeSample {
41786                    kind: sample_kind.unwrap_or_default(),
41787                    sample: Some(Box::new(sample)),
41788                })));
41789            }
41790        }
41791
41792        Ok(Some(Expression::AnalyzeStatistics(Box::new(
41793            AnalyzeStatistics {
41794                kind,
41795                option,
41796                this,
41797                expressions,
41798            },
41799        ))))
41800    }
41801
41802    /// parse_analyze_validate - Parses ANALYZE VALIDATE
41803    /// Python: parser.py:8034-8053
41804    pub fn parse_analyze_validate(&mut self) -> Result<Option<Expression>> {
41805        let mut kind = String::new();
41806        let mut this: Option<Box<Expression>> = None;
41807        let mut expression: Option<Box<Expression>> = None;
41808
41809        if self.match_text_seq(&["REF", "UPDATE"]) {
41810            kind = "REF".to_string();
41811            this = Some(Box::new(Expression::Identifier(Identifier::new(
41812                "UPDATE".to_string(),
41813            ))));
41814            if self.match_text_seq(&["SET", "DANGLING", "TO", "NULL"]) {
41815                this = Some(Box::new(Expression::Identifier(Identifier::new(
41816                    "UPDATE SET DANGLING TO NULL".to_string(),
41817                ))));
41818            }
41819        } else if self.match_text_seq(&["STRUCTURE"]) {
41820            kind = "STRUCTURE".to_string();
41821            if self.match_text_seq(&["CASCADE", "FAST"]) {
41822                this = Some(Box::new(Expression::Identifier(Identifier::new(
41823                    "CASCADE FAST".to_string(),
41824                ))));
41825            } else if self.match_text_seq(&["CASCADE", "COMPLETE"]) {
41826                if self.match_texts(&["ONLINE", "OFFLINE"]) {
41827                    let mode = self.previous().text.to_ascii_uppercase();
41828                    this = Some(Box::new(Expression::Identifier(Identifier::new(format!(
41829                        "CASCADE COMPLETE {}",
41830                        mode
41831                    )))));
41832                    expression = self.parse_into()?.map(Box::new);
41833                }
41834            }
41835        }
41836
41837        if kind.is_empty() {
41838            return Ok(None);
41839        }
41840
41841        Ok(Some(Expression::AnalyzeValidate(Box::new(
41842            AnalyzeValidate {
41843                kind,
41844                this,
41845                expression,
41846            },
41847        ))))
41848    }
41849
41850    /// parse_attach_detach - Parses ATTACH/DETACH statements (DuckDB)
41851    /// Python: DuckDB._parse_attach_detach
41852    pub fn parse_attach_detach(&mut self, is_attach: bool) -> Result<Expression> {
41853        // ATTACH [DATABASE] [IF NOT EXISTS] 'path' [AS alias] [(options)]
41854        // DETACH [DATABASE] [IF EXISTS] name
41855        // DATABASE can be tokenized as TokenType::Database (keyword), not just Var
41856        let _ = self.match_identifier("DATABASE") || self.match_token(TokenType::Database);
41857
41858        let exists = if is_attach {
41859            self.match_text_seq(&["IF", "NOT", "EXISTS"])
41860        } else {
41861            self.match_text_seq(&["IF", "EXISTS"])
41862        };
41863
41864        // Parse the expression (can be a path string, identifier, or expression like 'foo' || '.foo2'
41865        // or NOT EXISTS(subquery) for conditional attach)
41866        let this_expr = self.parse_expression()?;
41867
41868        // Check for AS alias
41869        let this = if self.match_token(TokenType::As) {
41870            let alias = self.expect_identifier_or_keyword_with_quoted()?;
41871            Expression::Alias(Box::new(Alias {
41872                this: this_expr,
41873                alias,
41874                column_aliases: Vec::new(),
41875                pre_alias_comments: Vec::new(),
41876                trailing_comments: Vec::new(),
41877                inferred_type: None,
41878            }))
41879        } else {
41880            this_expr
41881        };
41882
41883        if is_attach {
41884            // Parse optional (options)
41885            let expressions = if self.match_token(TokenType::LParen) {
41886                let mut opts = Vec::new();
41887                loop {
41888                    // Parse option: KEY [VALUE]
41889                    let key_name = self.advance().text.to_ascii_uppercase();
41890                    let key = Expression::Identifier(Identifier::new(key_name));
41891                    let value = if !self.check(TokenType::Comma) && !self.check(TokenType::RParen) {
41892                        // The value can be an identifier, string, boolean, etc.
41893                        let val_token = self.advance();
41894                        let val_expr = if val_token.token_type == TokenType::String {
41895                            Expression::Literal(Literal::String(val_token.text.clone()))
41896                        } else if val_token.token_type == TokenType::True {
41897                            Expression::Boolean(BooleanLiteral { value: true })
41898                        } else if val_token.token_type == TokenType::False {
41899                            Expression::Boolean(BooleanLiteral { value: false })
41900                        } else {
41901                            Expression::Identifier(Identifier::new(val_token.text.clone()))
41902                        };
41903                        Some(Box::new(val_expr))
41904                    } else {
41905                        None
41906                    };
41907                    opts.push(Expression::AttachOption(Box::new(AttachOption {
41908                        this: Box::new(key),
41909                        expression: value,
41910                    })));
41911                    if !self.match_token(TokenType::Comma) {
41912                        break;
41913                    }
41914                }
41915                self.expect(TokenType::RParen)?;
41916                opts
41917            } else {
41918                Vec::new()
41919            };
41920
41921            Ok(Expression::Attach(Box::new(Attach {
41922                this: Box::new(this),
41923                exists,
41924                expressions,
41925            })))
41926        } else {
41927            Ok(Expression::Detach(Box::new(Detach {
41928                this: Box::new(this),
41929                exists,
41930            })))
41931        }
41932    }
41933
41934    /// parse_install - Parses INSTALL statement (DuckDB)
41935    /// Python: DuckDB._parse_install
41936    pub fn parse_install(&mut self, force: bool) -> Result<Expression> {
41937        // INSTALL extension [FROM source]
41938        let name = self.expect_identifier_or_keyword()?;
41939        let this = Expression::Identifier(Identifier::new(name));
41940
41941        let from_ = if self.match_token(TokenType::From) {
41942            // FROM can be followed by a string or identifier
41943            Some(Box::new(self.parse_primary()?))
41944        } else {
41945            None
41946        };
41947
41948        Ok(Expression::Install(Box::new(Install {
41949            this: Box::new(this),
41950            from_,
41951            force: if force {
41952                Some(Box::new(Expression::Boolean(BooleanLiteral {
41953                    value: true,
41954                })))
41955            } else {
41956                None
41957            },
41958        })))
41959    }
41960
41961    /// parse_force_statement - Parses FORCE INSTALL/CHECKPOINT (DuckDB)
41962    /// Python: DuckDB._parse_force
41963    pub fn parse_force_statement(&mut self) -> Result<Expression> {
41964        if self.match_identifier("INSTALL") {
41965            return self.parse_install(true);
41966        }
41967        // FORCE CHECKPOINT or other: fallback to command
41968        self.parse_as_command()?
41969            .ok_or_else(|| self.parse_error("Failed to parse FORCE statement"))
41970    }
41971
41972    /// parse_summarize_statement - Parses SUMMARIZE statement (DuckDB)
41973    /// Python: DuckDB parser for SUMMARIZE
41974    pub fn parse_summarize_statement(&mut self) -> Result<Expression> {
41975        // SUMMARIZE [TABLE] expression
41976        let is_table = self.match_token(TokenType::Table);
41977
41978        // Try to parse a SELECT statement, string, or table reference
41979        let this = if self.check(TokenType::Select) || self.check(TokenType::With) {
41980            self.parse_select()?
41981        } else if self.check(TokenType::String) {
41982            self.parse_primary()?
41983        } else {
41984            // Parse as table name
41985            self.parse_table_parts()?
41986                .unwrap_or(Expression::Identifier(Identifier::new(String::new())))
41987        };
41988
41989        Ok(Expression::Summarize(Box::new(Summarize {
41990            this: Box::new(this),
41991            table: if is_table {
41992                Some(Box::new(Expression::Boolean(BooleanLiteral {
41993                    value: true,
41994                })))
41995            } else {
41996                None
41997            },
41998        })))
41999    }
42000
42001    /// parse_deallocate_prepare - Parses DEALLOCATE PREPARE <name>
42002    /// Presto/Trino syntax for deallocating prepared statements
42003    pub fn parse_deallocate_prepare(&mut self) -> Result<Expression> {
42004        self.skip(); // consume DEALLOCATE
42005
42006        // Check for PREPARE keyword
42007        if self.match_identifier("PREPARE") {
42008            // Parse the statement name
42009            let name = if !self.is_at_end() && !self.check(TokenType::Semicolon) {
42010                self.advance().text.clone()
42011            } else {
42012                String::new()
42013            };
42014
42015            // Build the command text
42016            let command_text = if name.is_empty() {
42017                "DEALLOCATE PREPARE".to_string()
42018            } else {
42019                format!("DEALLOCATE PREPARE {}", name)
42020            };
42021
42022            Ok(Expression::Command(Box::new(Command {
42023                this: command_text,
42024            })))
42025        } else {
42026            // Just DEALLOCATE without PREPARE - consume rest as command
42027            let mut parts = vec!["DEALLOCATE".to_string()];
42028            while !self.is_at_end() && !self.check(TokenType::Semicolon) {
42029                let token = self.advance();
42030                parts.push(token.text.clone());
42031            }
42032            Ok(Expression::Command(Box::new(Command {
42033                this: parts.join(" "),
42034            })))
42035        }
42036    }
42037
42038    /// parse_as_command - Creates Command expression
42039    #[allow(unused_variables, unused_mut)]
42040    /// parse_as_command - Parses remaining tokens as a raw command
42041    /// Python: _parse_as_command
42042    /// Used as fallback when specific parsing fails
42043    pub fn parse_as_command(&mut self) -> Result<Option<Expression>> {
42044        // Get the starting token text
42045        let start_text = if self.current > 0 {
42046            self.tokens
42047                .get(self.current - 1)
42048                .map(|t| t.text.clone())
42049                .unwrap_or_default()
42050        } else {
42051            String::new()
42052        };
42053
42054        // Consume all remaining tokens, storing both text and type
42055        let mut tokens_info: Vec<(String, TokenType)> = Vec::new();
42056        while !self.is_at_end() {
42057            let token = self.advance();
42058            tokens_info.push((token.text.clone(), token.token_type.clone()));
42059        }
42060
42061        // Join tokens intelligently, avoiding spaces around punctuation
42062        let mut expression = String::new();
42063        for (i, (text, token_type)) in tokens_info.iter().enumerate() {
42064            if i > 0 {
42065                // Check if we should add a space before this token
42066                let prev_type = &tokens_info[i - 1].1;
42067                let needs_space = !Self::is_punctuation_token(prev_type)
42068                    && !Self::is_punctuation_token(token_type);
42069                if needs_space {
42070                    expression.push(' ');
42071                }
42072            }
42073            expression.push_str(text);
42074        }
42075
42076        Ok(Some(Expression::Command(Box::new(Command {
42077            this: if expression.is_empty() {
42078                start_text
42079            } else {
42080                format!("{} {}", start_text, expression)
42081            },
42082        }))))
42083    }
42084
42085    /// Helper to determine if a token type is punctuation that shouldn't have spaces around it
42086    fn is_punctuation_token(token_type: &TokenType) -> bool {
42087        matches!(
42088            token_type,
42089            TokenType::Dot | TokenType::Colon | TokenType::DColon
42090        )
42091    }
42092
42093    /// Fallback to Command expression from a saved position.
42094    /// Extracts verbatim SQL text from source if available, consuming tokens until semicolon/EOF.
42095    fn fallback_to_command(&mut self, start_pos: usize) -> Result<Expression> {
42096        let start_span = self.tokens[start_pos].span.start;
42097        // Consume until semicolon or end
42098        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
42099            self.skip();
42100        }
42101        let command_text = if let Some(ref source) = self.source {
42102            let end_span = if self.current > 0 {
42103                self.tokens[self.current - 1].span.end
42104            } else {
42105                start_span
42106            };
42107            source[start_span..end_span].trim().to_string()
42108        } else {
42109            // Fallback: join token texts
42110            let mut parts = Vec::new();
42111            for i in start_pos..self.current {
42112                if self.tokens[i].token_type == TokenType::String {
42113                    parts.push(format!("'{}'", self.tokens[i].text.replace('\'', "''")));
42114                } else {
42115                    parts.push(self.tokens[i].text.clone());
42116                }
42117            }
42118            parts.join(" ")
42119        };
42120        Ok(Expression::Command(Box::new(Command {
42121            this: command_text,
42122        })))
42123    }
42124
42125    /// parse_assignment - Parses assignment expressions (variable := value)
42126    /// Python: _parse_assignment
42127    pub fn parse_assignment(&mut self) -> Result<Option<Expression>> {
42128        // First parse a disjunction (left side of potential assignment)
42129        let mut this = self.parse_disjunction()?;
42130
42131        // Handle := assignment operator
42132        while self.match_token(TokenType::ColonEq) {
42133            if let Some(left) = this {
42134                let right = self.parse_assignment()?;
42135                if let Some(right_expr) = right {
42136                    this = Some(Expression::PropertyEQ(Box::new(BinaryOp {
42137                        left,
42138                        right: right_expr,
42139                        left_comments: Vec::new(),
42140                        operator_comments: Vec::new(),
42141                        trailing_comments: Vec::new(),
42142                        inferred_type: None,
42143                    })));
42144                } else {
42145                    this = Some(left);
42146                    break;
42147                }
42148            } else {
42149                break;
42150            }
42151        }
42152
42153        // ClickHouse ternary operator: condition ? true_value : false_value
42154        // Parsed as: If(this=condition, true=true_value, false=false_value)
42155        if matches!(
42156            self.config.dialect,
42157            Some(crate::dialects::DialectType::ClickHouse)
42158        ) {
42159            if let Some(condition) = this {
42160                if self.match_token(TokenType::Parameter) {
42161                    if self.check(TokenType::Colon) {
42162                        return Err(self.parse_error(
42163                            "Expected true expression after ? in ClickHouse ternary",
42164                        ));
42165                    }
42166                    let true_value = self.parse_assignment()?.ok_or_else(|| {
42167                        self.parse_error("Expected true expression after ? in ClickHouse ternary")
42168                    })?;
42169                    let false_value = if self.match_token(TokenType::Colon) {
42170                        self.parse_assignment()?.unwrap_or(Expression::Null(Null))
42171                    } else {
42172                        Expression::Null(Null)
42173                    };
42174                    return Ok(Some(Expression::IfFunc(Box::new(IfFunc {
42175                        original_name: None,
42176                        condition,
42177                        true_value,
42178                        false_value: Some(false_value),
42179                        inferred_type: None,
42180                    }))));
42181                }
42182                this = Some(condition);
42183            }
42184        }
42185
42186        Ok(this)
42187    }
42188
42189    /// parse_auto_increment - Implemented from Python _parse_auto_increment
42190    /// Calls: parse_bitwise
42191    #[allow(unused_variables, unused_mut)]
42192    pub fn parse_auto_increment(&mut self) -> Result<Option<Expression>> {
42193        if self.match_text_seq(&["START"]) {
42194            return Ok(Some(Expression::GeneratedAsIdentityColumnConstraint(
42195                Box::new(GeneratedAsIdentityColumnConstraint {
42196                    this: None,
42197                    expression: None,
42198                    on_null: None,
42199                    start: None,
42200                    increment: None,
42201                    minvalue: None,
42202                    maxvalue: None,
42203                    cycle: None,
42204                    order: None,
42205                }),
42206            )));
42207        }
42208        if self.match_text_seq(&["INCREMENT"]) {
42209            // Matched: INCREMENT
42210            return Ok(None);
42211        }
42212        if self.match_text_seq(&["ORDER"]) {
42213            // Matched: ORDER
42214            return Ok(None);
42215        }
42216        Ok(None)
42217    }
42218
42219    /// parse_auto_property - Implemented from Python _parse_auto_property
42220    #[allow(unused_variables, unused_mut)]
42221    pub fn parse_auto_property(&mut self) -> Result<Option<Expression>> {
42222        if self.match_text_seq(&["REFRESH"]) {
42223            // Matched: REFRESH
42224            return Ok(None);
42225        }
42226        Ok(None)
42227    }
42228
42229    /// parse_between - Implemented from Python _parse_between
42230    #[allow(unused_variables, unused_mut)]
42231    pub fn parse_between(&mut self) -> Result<Option<Expression>> {
42232        if self.match_text_seq(&["SYMMETRIC"]) {
42233            // Matched: SYMMETRIC
42234            return Ok(None);
42235        }
42236        if self.match_text_seq(&["ASYMMETRIC"]) {
42237            // Matched: ASYMMETRIC
42238            return Ok(None);
42239        }
42240        Ok(None)
42241    }
42242
42243    /// parse_bitwise - Parses bitwise OR/XOR/AND expressions
42244    /// Python: _parse_bitwise
42245    /// Delegates to the existing parse_bitwise_or in the operator precedence chain
42246    pub fn parse_bitwise(&mut self) -> Result<Option<Expression>> {
42247        let start = self.current;
42248        match self.parse_bitwise_or() {
42249            Ok(expr) => Ok(Some(expr)),
42250            Err(_err) if self.current == start => Ok(None),
42251            Err(err) => Err(err),
42252        }
42253    }
42254
42255    /// parse_blockcompression - Implemented from Python _parse_blockcompression
42256    #[allow(unused_variables, unused_mut)]
42257    pub fn parse_blockcompression(&mut self) -> Result<Option<Expression>> {
42258        if self.match_text_seq(&["ALWAYS"]) {
42259            return Ok(Some(Expression::BlockCompressionProperty(Box::new(
42260                BlockCompressionProperty {
42261                    autotemp: None,
42262                    always: None,
42263                    default: None,
42264                    manual: None,
42265                    never: None,
42266                },
42267            ))));
42268        }
42269        if self.match_text_seq(&["MANUAL"]) {
42270            // Matched: MANUAL
42271            return Ok(None);
42272        }
42273        Ok(None)
42274    }
42275
42276    /// parse_boolean - Parse boolean literal (TRUE/FALSE)
42277    /// Python: if self._match(TokenType.TRUE): return exp.Boolean(this=True)
42278    pub fn parse_boolean(&mut self) -> Result<Option<Expression>> {
42279        if self.match_token(TokenType::True) {
42280            return Ok(Some(Expression::Boolean(BooleanLiteral { value: true })));
42281        }
42282        if self.match_token(TokenType::False) {
42283            return Ok(Some(Expression::Boolean(BooleanLiteral { value: false })));
42284        }
42285        Ok(None)
42286    }
42287
42288    /// parse_bracket - Ported from Python _parse_bracket
42289    /// Parses bracket expressions: array[index], array literal [1,2,3], or struct {key: value}
42290    #[allow(unused_variables, unused_mut)]
42291    pub fn parse_bracket(&mut self) -> Result<Option<Expression>> {
42292        self.parse_bracket_with_expr(None)
42293    }
42294
42295    /// parse_bracket_with_expr - Parses bracket with optional left-side expression
42296    fn parse_bracket_with_expr(&mut self, this: Option<Expression>) -> Result<Option<Expression>> {
42297        // Check for [ or {
42298        let is_bracket = self.match_token(TokenType::LBracket);
42299        let is_brace = if !is_bracket {
42300            self.match_token(TokenType::LBrace)
42301        } else {
42302            false
42303        };
42304
42305        if !is_bracket && !is_brace {
42306            return Ok(this);
42307        }
42308
42309        // Parse comma-separated expressions inside brackets
42310        let mut expressions: Vec<Expression> = Vec::new();
42311
42312        if is_bracket && !self.check(TokenType::RBracket) {
42313            // Check for slice syntax at the start: [:...] or [:-...]
42314            // This needs to be detected before parse_bracket_key_value which calls parse_primary
42315            // and parse_primary would consume : as a parameter prefix
42316            let first_expr = if self.check(TokenType::Colon) {
42317                // This is slice syntax like [:] or [:-1] or [::step]
42318                // Parse it using slice parser with no 'this'
42319                if let Some(slice) = self.parse_slice()? {
42320                    slice
42321                } else {
42322                    self.parse_expression()?
42323                }
42324            } else if let Ok(Some(expr)) = self.parse_bracket_key_value() {
42325                expr
42326            } else {
42327                // Parse regular expression and check for slice
42328                let expr = self.parse_expression()?;
42329                // Check if followed by colon (slice syntax like [start:end])
42330                if self.check(TokenType::Colon) {
42331                    if let Some(slice) = self.parse_slice_with_this(Some(expr))? {
42332                        slice
42333                    } else {
42334                        return Err(self.parse_error("Failed to parse slice"));
42335                    }
42336                } else {
42337                    expr
42338                }
42339            };
42340
42341            // Check for comprehension syntax: [expr FOR var IN iterator [IF condition]]
42342            if self.match_token(TokenType::For) {
42343                // Parse loop variable - typically a simple identifier like 'x'
42344                let loop_var = self.parse_primary()?;
42345
42346                // Parse optional position (second variable after comma)
42347                let position = if self.match_token(TokenType::Comma) {
42348                    Some(self.parse_primary()?)
42349                } else {
42350                    None
42351                };
42352
42353                // Expect IN keyword
42354                if !self.match_token(TokenType::In) {
42355                    return Err(self.parse_error("Expected IN in comprehension"));
42356                }
42357
42358                // Parse iterator expression
42359                let iterator = self.parse_expression()?;
42360
42361                // Parse optional condition after IF
42362                let condition = if self.match_token(TokenType::If) {
42363                    Some(self.parse_expression()?)
42364                } else {
42365                    None
42366                };
42367
42368                // Expect closing bracket
42369                self.expect(TokenType::RBracket)?;
42370
42371                // Return Comprehension wrapped in an expression
42372                return Ok(Some(Expression::Comprehension(Box::new(Comprehension {
42373                    this: Box::new(first_expr),
42374                    expression: Box::new(loop_var),
42375                    position: position.map(Box::new),
42376                    iterator: Some(Box::new(iterator)),
42377                    condition: condition.map(Box::new),
42378                }))));
42379            }
42380
42381            expressions.push(first_expr);
42382
42383            // Continue parsing remaining expressions
42384            while self.match_token(TokenType::Comma) {
42385                if let Ok(Some(expr)) = self.parse_bracket_key_value() {
42386                    expressions.push(expr);
42387                } else {
42388                    match self.parse_expression() {
42389                        Ok(expr) => expressions.push(expr),
42390                        Err(_) => break,
42391                    }
42392                }
42393            }
42394        } else if is_brace && !self.check(TokenType::RBrace) {
42395            loop {
42396                if let Ok(Some(expr)) = self.parse_bracket_key_value() {
42397                    expressions.push(expr);
42398                } else {
42399                    match self.parse_expression() {
42400                        Ok(expr) => expressions.push(expr),
42401                        Err(_) => break,
42402                    }
42403                }
42404                if !self.match_token(TokenType::Comma) {
42405                    break;
42406                }
42407            }
42408        }
42409
42410        // Expect closing bracket
42411        if is_bracket {
42412            self.expect(TokenType::RBracket)?;
42413        } else if is_brace {
42414            self.expect(TokenType::RBrace)?;
42415        }
42416
42417        // Build the result
42418        if is_brace {
42419            // Struct literal: {key: value, ...}
42420            // Convert expressions to (Option<name>, expr) pairs
42421            let fields: Vec<(Option<String>, Expression)> =
42422                expressions.into_iter().map(|e| (None, e)).collect();
42423            Ok(Some(Expression::Struct(Box::new(Struct { fields }))))
42424        } else if let Some(base_expr) = this {
42425            // Subscript access: base[index]
42426            if expressions.len() == 1 {
42427                Ok(Some(Expression::Subscript(Box::new(Subscript {
42428                    this: base_expr,
42429                    index: expressions.remove(0),
42430                }))))
42431            } else {
42432                // Multiple indices - create nested subscripts or array
42433                let mut result = base_expr;
42434                for expr in expressions {
42435                    result = Expression::Subscript(Box::new(Subscript {
42436                        this: result,
42437                        index: expr,
42438                    }));
42439                }
42440                Ok(Some(result))
42441            }
42442        } else {
42443            // Array literal: [1, 2, 3]
42444            Ok(Some(Expression::Array(Box::new(Array { expressions }))))
42445        }
42446    }
42447
42448    /// parse_bracket_key_value - Ported from Python _parse_bracket_key_value
42449    /// Parses key-value pairs in brackets: key: value or key => value
42450    #[allow(unused_variables, unused_mut)]
42451    pub fn parse_bracket_key_value(&mut self) -> Result<Option<Expression>> {
42452        let saved_pos = self.current;
42453
42454        // Try to parse as key: value or key => value
42455        if let Ok(key) = self.parse_primary() {
42456            // Check for : or =>
42457            if self.match_token(TokenType::Colon) || self.match_text_seq(&["=>"]) {
42458                match self.parse_expression() {
42459                    Ok(value) => {
42460                        // Return as NamedArgument for key-value pair
42461                        // Extract the name from the key (identifier or string literal)
42462                        let name = match &key {
42463                            Expression::Identifier(id) => id.clone(),
42464                            Expression::Literal(crate::expressions::Literal::String(s)) => {
42465                                Identifier::new(s.clone())
42466                            }
42467                            _ => Identifier::new("".to_string()),
42468                        };
42469                        return Ok(Some(Expression::NamedArgument(Box::new(NamedArgument {
42470                            name,
42471                            value,
42472                            separator: NamedArgSeparator::DArrow, // Using DArrow for colon-style key: value
42473                        }))));
42474                    }
42475                    Err(_) => {
42476                        self.current = saved_pos;
42477                        return Ok(None);
42478                    }
42479                }
42480            }
42481            self.current = saved_pos;
42482        }
42483
42484        Ok(None)
42485    }
42486
42487    /// parse_ceil_floor - Implemented from Python _parse_ceil_floor
42488    /// Calls: parse_lambda, parse_var
42489    #[allow(unused_variables, unused_mut)]
42490    pub fn parse_ceil_floor(&mut self) -> Result<Option<Expression>> {
42491        if self.match_text_seq(&["TO"]) {
42492            // Matched: TO
42493            return Ok(None);
42494        }
42495        Ok(None)
42496    }
42497
42498    /// parse_changes - Implemented from Python _parse_changes
42499    /// Parses: CHANGES(INFORMATION => var) AT|BEFORE(...) END(...)
42500    pub fn parse_changes(&mut self) -> Result<Option<Expression>> {
42501        // Match: CHANGES(INFORMATION =>
42502        if !self.match_text_seq(&["CHANGES", "(", "INFORMATION", "=>"]) {
42503            return Ok(None);
42504        }
42505
42506        // Parse information (any token as var)
42507        let information = self.parse_var()?.map(Box::new);
42508
42509        // Match closing paren
42510        self.match_token(TokenType::RParen);
42511
42512        // Parse at_before (Snowflake AT/BEFORE clause)
42513        let at_before = self.parse_historical_data()?.map(Box::new);
42514
42515        // Parse end (optional second historical data clause)
42516        let end = self.parse_historical_data()?.map(Box::new);
42517
42518        Ok(Some(Expression::Changes(Box::new(Changes {
42519            information,
42520            at_before,
42521            end,
42522        }))))
42523    }
42524
42525    /// parse_char - Parses CHAR/CHR function with optional USING charset
42526    /// Python: CHAR(args...) [USING charset]
42527    /// MySQL: CHAR(n1, n2, ... USING charset)
42528    pub fn parse_char(&mut self) -> Result<Option<Expression>> {
42529        // Parse expressions inside CHAR()
42530        let mut args = Vec::new();
42531        loop {
42532            let expr = self.parse_expression()?;
42533            args.push(expr);
42534            if !self.match_token(TokenType::Comma) {
42535                break;
42536            }
42537        }
42538
42539        // Check for USING charset
42540        let charset = if self.match_token(TokenType::Using) {
42541            self.parse_var()?.map(|v| {
42542                if let Expression::Identifier(id) = v {
42543                    id.name
42544                } else {
42545                    String::new()
42546                }
42547            })
42548        } else {
42549            None
42550        };
42551
42552        if args.is_empty() {
42553            return Ok(None);
42554        }
42555
42556        // If there's a charset or multiple args, use CharFunc (MySQL-style)
42557        // Otherwise use simple Chr for single-arg CHR function
42558        if charset.is_some() || args.len() > 1 {
42559            Ok(Some(Expression::CharFunc(Box::new(
42560                crate::expressions::CharFunc {
42561                    args,
42562                    charset,
42563                    name: None, // defaults to CHAR
42564                },
42565            ))))
42566        } else {
42567            Ok(Some(Expression::Chr(Box::new(UnaryFunc::new(
42568                args.into_iter().next().unwrap(),
42569            )))))
42570        }
42571    }
42572
42573    /// parse_character_set - Ported from Python _parse_character_set
42574    #[allow(unused_variables, unused_mut)]
42575    /// parse_character_set - Parses CHARACTER SET property
42576    /// Example: CHARACTER SET = utf8 or CHARACTER SET utf8mb4
42577    pub fn parse_character_set(&mut self) -> Result<Option<Expression>> {
42578        // Optional = sign
42579        self.match_token(TokenType::Eq);
42580
42581        // Parse the charset name (variable or string)
42582        let charset = self.parse_var_or_string()?;
42583        if charset.is_none() {
42584            return Ok(None);
42585        }
42586
42587        Ok(Some(Expression::CharacterSetProperty(Box::new(
42588            CharacterSetProperty {
42589                this: Box::new(charset.unwrap()),
42590                default: None,
42591            },
42592        ))))
42593    }
42594
42595    /// parse_checksum - Implemented from Python _parse_checksum
42596    #[allow(unused_variables, unused_mut)]
42597    pub fn parse_checksum(&mut self) -> Result<Option<Expression>> {
42598        if self.match_text_seq(&["OFF"]) {
42599            return Ok(Some(Expression::ChecksumProperty(Box::new(
42600                ChecksumProperty {
42601                    on: None,
42602                    default: None,
42603                },
42604            ))));
42605        }
42606        Ok(None)
42607    }
42608
42609    /// parse_cluster - CLUSTER BY clause for Hive/Spark-style queries
42610    /// Parses a list of ordered expressions (columns with optional ASC/DESC)
42611    #[allow(unused_variables, unused_mut)]
42612    pub fn parse_cluster(&mut self) -> Result<Option<Expression>> {
42613        let mut expressions: Vec<Ordered> = Vec::new();
42614
42615        loop {
42616            // Parse an ordered expression (column with optional direction)
42617            if let Some(ordered) = self.parse_ordered_item()? {
42618                expressions.push(ordered);
42619            } else {
42620                break;
42621            }
42622
42623            if !self.match_token(TokenType::Comma) {
42624                break;
42625            }
42626        }
42627
42628        if expressions.is_empty() {
42629            return Ok(None);
42630        }
42631
42632        Ok(Some(Expression::ClusterBy(Box::new(ClusterBy {
42633            expressions,
42634        }))))
42635    }
42636
42637    /// parse_clustered_by - Implemented from Python _parse_clustered_by
42638    #[allow(unused_variables, unused_mut)]
42639    pub fn parse_clustered_by(&mut self) -> Result<Option<Expression>> {
42640        if self.match_text_seq(&["BY"]) {
42641            return Ok(Some(Expression::ClusteredByProperty(Box::new(
42642                ClusteredByProperty {
42643                    expressions: Vec::new(),
42644                    sorted_by: None,
42645                    buckets: None,
42646                },
42647            ))));
42648        }
42649        if self.match_text_seq(&["SORTED", "BY"]) {
42650            // Matched: SORTED BY
42651            return Ok(None);
42652        }
42653        Ok(None)
42654    }
42655
42656    /// Parse Snowflake colon JSON path extraction: data:field or data:field.subfield
42657    /// Python: def _parse_colon_as_variant_extract(self, this)
42658    pub fn parse_colon_as_variant_extract(
42659        &mut self,
42660        this: Expression,
42661    ) -> Result<Option<Expression>> {
42662        // Build a JSON path from colon-separated identifiers
42663        // Track whether each segment was quoted (needs bracket notation for spaces/special chars)
42664        let mut json_path: Vec<(String, bool)> = Vec::new();
42665
42666        while self.match_token(TokenType::Colon) {
42667            // Parse the path segment (field name)
42668            if let Some(field) = self.parse_identifier()? {
42669                if let Expression::Identifier(ident) = field {
42670                    json_path.push((
42671                        ident.name.clone(),
42672                        ident.quoted || ident.name.contains(' ') || ident.name.contains('\''),
42673                    ));
42674                }
42675            }
42676
42677            // Check for dot-separated sub-paths
42678            while self.match_token(TokenType::Dot) {
42679                if let Some(subfield) = self.parse_identifier()? {
42680                    if let Expression::Identifier(ident) = subfield {
42681                        json_path.push((
42682                            ident.name.clone(),
42683                            ident.quoted || ident.name.contains(' ') || ident.name.contains('\''),
42684                        ));
42685                    }
42686                }
42687            }
42688        }
42689
42690        if json_path.is_empty() {
42691            return Ok(Some(this));
42692        }
42693
42694        // Build the JSON path expression string
42695        // Use bracket notation for segments with spaces/special chars: a["b c"]
42696        // Use dot notation for simple segments: a.b.c
42697        let mut path_str = String::new();
42698        for (i, (segment, needs_bracket)) in json_path.iter().enumerate() {
42699            if *needs_bracket {
42700                // Bracket notation: ["key with spaces"]
42701                path_str.push('[');
42702                path_str.push('"');
42703                path_str.push_str(segment);
42704                path_str.push('"');
42705                path_str.push(']');
42706            } else {
42707                if i > 0 {
42708                    path_str.push('.');
42709                }
42710                path_str.push_str(segment);
42711            }
42712        }
42713
42714        Ok(Some(Expression::JSONExtract(Box::new(JSONExtract {
42715            this: Box::new(this),
42716            expression: Box::new(Expression::Literal(Literal::String(path_str))),
42717            only_json_types: None,
42718            expressions: Vec::new(),
42719            variant_extract: Some(Box::new(Expression::Boolean(BooleanLiteral {
42720                value: true,
42721            }))),
42722            json_query: None,
42723            option: None,
42724            quote: None,
42725            on_condition: None,
42726            requires_json: None,
42727        }))))
42728    }
42729
42730    /// parse_column - Parse column expression
42731    /// Python: this = self._parse_column_reference(); return self._parse_column_ops(this)
42732    pub fn parse_column(&mut self) -> Result<Option<Expression>> {
42733        // Parse column reference (field name that becomes a Column expression)
42734        let column_ref = self.parse_column_reference()?;
42735        if column_ref.is_some() {
42736            // Apply column ops (bracket subscript, property access with dots, casts)
42737            return self.parse_column_ops_with_expr(column_ref);
42738        }
42739        // Try parsing bracket directly if no column reference
42740        self.parse_bracket()
42741    }
42742
42743    /// parse_column_constraint - Ported from Python _parse_column_constraint
42744    /// Parses column-level constraints like NOT NULL, PRIMARY KEY, UNIQUE, DEFAULT, CHECK, etc.
42745    #[allow(unused_variables, unused_mut)]
42746    pub fn parse_column_constraint(&mut self) -> Result<Option<Expression>> {
42747        // Check for optional CONSTRAINT keyword and name
42748        let constraint_name = if self.match_token(TokenType::Constraint) {
42749            self.parse_id_var()?.and_then(|e| {
42750                if let Expression::Identifier(id) = e {
42751                    Some(id)
42752                } else {
42753                    None
42754                }
42755            })
42756        } else {
42757            None
42758        };
42759
42760        // NOT NULL
42761        if self.match_text_seq(&["NOT", "NULL"]) {
42762            return Ok(Some(Expression::NotNullColumnConstraint(Box::new(
42763                NotNullColumnConstraint { allow_null: None },
42764            ))));
42765        }
42766
42767        // NOT FOR REPLICATION (SQL Server) - must be before NULL check
42768        if self.match_text_seq(&["NOT", "FOR", "REPLICATION"]) {
42769            return Ok(Some(Expression::Property(Box::new(
42770                crate::expressions::Property {
42771                    this: Box::new(Expression::Identifier(Identifier::new(
42772                        "NOT FOR REPLICATION".to_string(),
42773                    ))),
42774                    value: None,
42775                },
42776            ))));
42777        }
42778
42779        // NULL
42780        if self.match_text_seq(&["NULL"]) {
42781            return Ok(Some(Expression::NotNullColumnConstraint(Box::new(
42782                NotNullColumnConstraint {
42783                    allow_null: Some(Box::new(Expression::Boolean(BooleanLiteral {
42784                        value: true,
42785                    }))),
42786                },
42787            ))));
42788        }
42789
42790        // PRIMARY KEY
42791        if self.match_text_seq(&["PRIMARY", "KEY"]) {
42792            return Ok(Some(Expression::PrimaryKeyColumnConstraint(Box::new(
42793                PrimaryKeyColumnConstraint {
42794                    desc: None,
42795                    options: Vec::new(),
42796                },
42797            ))));
42798        }
42799
42800        // UNIQUE
42801        if self.match_text_seq(&["UNIQUE"]) {
42802            // Check for optional KEY/INDEX
42803            let _ = self.match_texts(&["KEY", "INDEX"]);
42804            // Check for NULLS NOT DISTINCT (PostgreSQL 15+ feature)
42805            let nulls = if self.match_text_seq(&["NULLS", "NOT", "DISTINCT"]) {
42806                Some(Box::new(Expression::Boolean(BooleanLiteral {
42807                    value: true,
42808                })))
42809            } else {
42810                None
42811            };
42812            return Ok(Some(Expression::UniqueColumnConstraint(Box::new(
42813                UniqueColumnConstraint {
42814                    this: None,
42815                    index_type: None,
42816                    on_conflict: None,
42817                    nulls,
42818                    options: Vec::new(),
42819                },
42820            ))));
42821        }
42822
42823        // DEFAULT
42824        if self.match_text_seq(&["DEFAULT"]) {
42825            let default_value = self.parse_select_or_expression()?;
42826            if let Some(val) = default_value {
42827                return Ok(Some(Expression::DefaultColumnConstraint(Box::new(
42828                    DefaultColumnConstraint {
42829                        this: Box::new(val),
42830                    },
42831                ))));
42832            }
42833            return Ok(None);
42834        }
42835
42836        // CHECK
42837        if self.match_text_seq(&["CHECK"]) {
42838            if self.match_token(TokenType::LParen) {
42839                let expr = self.parse_select_or_expression()?;
42840                self.match_token(TokenType::RParen);
42841                if let Some(check_expr) = expr {
42842                    return Ok(Some(Expression::CheckColumnConstraint(Box::new(
42843                        CheckColumnConstraint {
42844                            this: Box::new(check_expr),
42845                            enforced: None,
42846                        },
42847                    ))));
42848                }
42849            }
42850            return Ok(None);
42851        }
42852
42853        // REFERENCES (foreign key)
42854        if self.match_text_seq(&["REFERENCES"]) {
42855            let table = self.parse_table_parts()?;
42856            let columns = if self.match_token(TokenType::LParen) {
42857                let mut cols = Vec::new();
42858                loop {
42859                    if let Some(col) = self.parse_id_var()? {
42860                        cols.push(col);
42861                    }
42862                    if !self.match_token(TokenType::Comma) {
42863                        break;
42864                    }
42865                }
42866                self.match_token(TokenType::RParen);
42867                cols
42868            } else {
42869                Vec::new()
42870            };
42871
42872            return Ok(Some(Expression::ForeignKey(Box::new(ForeignKey {
42873                expressions: columns,
42874                reference: table.map(Box::new),
42875                delete: None,
42876                update: None,
42877                options: Vec::new(),
42878            }))));
42879        }
42880
42881        // AUTO_INCREMENT / AUTOINCREMENT / IDENTITY
42882        if self.match_texts(&["AUTO_INCREMENT", "AUTOINCREMENT", "IDENTITY"]) {
42883            // Check for IDENTITY(start, increment) or IDENTITY START x INCREMENT y syntax
42884            let mut start = None;
42885            let mut increment = None;
42886
42887            if self.match_token(TokenType::LParen) {
42888                // Parse (start, increment)
42889                start = self.parse_bitwise()?;
42890                if self.match_token(TokenType::Comma) {
42891                    increment = self.parse_bitwise()?;
42892                }
42893                self.expect(TokenType::RParen)?;
42894            } else if self.match_text_seq(&["START"]) {
42895                // Parse START x INCREMENT y
42896                start = self.parse_bitwise()?;
42897                if self.match_text_seq(&["INCREMENT"]) {
42898                    increment = self.parse_bitwise()?;
42899                }
42900            }
42901
42902            if start.is_some() || increment.is_some() {
42903                return Ok(Some(Expression::GeneratedAsIdentityColumnConstraint(
42904                    Box::new(GeneratedAsIdentityColumnConstraint {
42905                        this: Some(Box::new(Expression::Boolean(BooleanLiteral {
42906                            value: false,
42907                        }))),
42908                        expression: None,
42909                        on_null: None,
42910                        start: start.map(Box::new),
42911                        increment: increment.map(Box::new),
42912                        minvalue: None,
42913                        maxvalue: None,
42914                        cycle: None,
42915                        order: None,
42916                    }),
42917                )));
42918            }
42919            return Ok(Some(Expression::AutoIncrementColumnConstraint(
42920                AutoIncrementColumnConstraint,
42921            )));
42922        }
42923
42924        // COMMENT 'text' - CommentColumnConstraint is a unit struct, use a different expression
42925        if self.match_text_seq(&["COMMENT"]) {
42926            if let Some(comment) = self.parse_string()? {
42927                // Use CommentColumnConstraint unit struct
42928                return Ok(Some(Expression::CommentColumnConstraint(
42929                    CommentColumnConstraint,
42930                )));
42931            }
42932            return Ok(None);
42933        }
42934
42935        // COLLATE collation_name - use CollateProperty instead
42936        if self.match_text_seq(&["COLLATE"]) {
42937            if let Some(collation) = self.parse_id_var()? {
42938                return Ok(Some(Expression::CollateProperty(Box::new(
42939                    CollateProperty {
42940                        this: Box::new(collation),
42941                        default: None,
42942                    },
42943                ))));
42944            }
42945            return Ok(None);
42946        }
42947
42948        // ClickHouse dictionary column attributes: HIERARCHICAL, IS_OBJECT_ID, INJECTIVE
42949        if matches!(
42950            self.config.dialect,
42951            Some(crate::dialects::DialectType::ClickHouse)
42952        ) {
42953            if self.match_texts(&["HIERARCHICAL", "IS_OBJECT_ID", "INJECTIVE"]) {
42954                let attr_name = self.previous().text.to_ascii_uppercase();
42955                return Ok(Some(Expression::Property(Box::new(
42956                    crate::expressions::Property {
42957                        this: Box::new(Expression::Identifier(Identifier::new(attr_name))),
42958                        value: None,
42959                    },
42960                ))));
42961            }
42962            // ClickHouse EXPRESSION expr and ALIAS expr (dictionary column attributes)
42963            if self.match_texts(&["EXPRESSION"]) {
42964                let expr = self.parse_expression()?;
42965                return Ok(Some(Expression::DefaultColumnConstraint(Box::new(
42966                    DefaultColumnConstraint {
42967                        this: Box::new(expr),
42968                    },
42969                ))));
42970            }
42971        }
42972
42973        // GENERATED ... AS IDENTITY
42974        if self.match_text_seq(&["GENERATED"]) {
42975            let always = self.match_text_seq(&["ALWAYS"]);
42976            if !always {
42977                self.match_text_seq(&["BY", "DEFAULT"]);
42978            }
42979            let on_null = self.match_text_seq(&["ON", "NULL"]);
42980            if self.match_text_seq(&["AS", "IDENTITY"]) {
42981                return Ok(Some(Expression::GeneratedAsIdentityColumnConstraint(
42982                    Box::new(GeneratedAsIdentityColumnConstraint {
42983                        this: None,
42984                        expression: None,
42985                        on_null: if on_null {
42986                            Some(Box::new(Expression::Boolean(BooleanLiteral {
42987                                value: true,
42988                            })))
42989                        } else {
42990                            None
42991                        },
42992                        start: None,
42993                        increment: None,
42994                        minvalue: None,
42995                        maxvalue: None,
42996                        cycle: None,
42997                        order: None,
42998                    }),
42999                )));
43000            }
43001            return Ok(None);
43002        }
43003
43004        // PATH 'xpath' - for XMLTABLE/JSON_TABLE columns
43005        if self.match_text_seq(&["PATH"]) {
43006            if let Some(path_expr) = self.parse_string()? {
43007                return Ok(Some(Expression::PathColumnConstraint(Box::new(
43008                    PathColumnConstraint {
43009                        this: Box::new(path_expr),
43010                    },
43011                ))));
43012            }
43013            return Ok(None);
43014        }
43015
43016        // Return the constraint name if we matched CONSTRAINT but no actual constraint
43017        if let Some(name) = constraint_name {
43018            return Ok(Some(Expression::Identifier(name)));
43019        }
43020
43021        Ok(None)
43022    }
43023
43024    /// parse_column_def_with_exists - Ported from Python _parse_column_def_with_exists
43025    /// Parses a column definition with optional IF [NOT] EXISTS clause
43026    #[allow(unused_variables, unused_mut)]
43027    pub fn parse_column_def_with_exists(&mut self) -> Result<Option<Expression>> {
43028        let start = self.current;
43029
43030        // Optionally match COLUMN keyword
43031        let _ = self.match_text_seq(&["COLUMN"]);
43032
43033        // Check for IF NOT EXISTS
43034        let not_exists = self.match_text_seq(&["IF", "NOT", "EXISTS"]);
43035        let exists = if !not_exists {
43036            self.match_text_seq(&["IF", "EXISTS"])
43037        } else {
43038            false
43039        };
43040
43041        // Parse the field definition
43042        let expression = self.parse_field_def()?;
43043
43044        if expression.is_none() {
43045            self.current = start;
43046            return Ok(None);
43047        }
43048
43049        // If it's a ColumnDef, we're good
43050        if let Some(Expression::ColumnDef(ref _col_def)) = expression {
43051            // The exists flag would be set on the ColumnDef, but our struct doesn't have that field
43052            // Just return the expression as-is
43053            return Ok(expression);
43054        }
43055
43056        // Not a ColumnDef, backtrack
43057        self.current = start;
43058        Ok(None)
43059    }
43060
43061    /// parse_column_ops - Parses column operations (stub for compatibility)
43062    pub fn parse_column_ops(&mut self) -> Result<Option<Expression>> {
43063        self.parse_column_ops_with_expr(None)
43064    }
43065
43066    /// parse_column_ops_with_expr - Parses column operations (dot access, brackets, casts)
43067    /// Python: _parse_column_ops(this)
43068    pub fn parse_column_ops_with_expr(
43069        &mut self,
43070        this: Option<Expression>,
43071    ) -> Result<Option<Expression>> {
43072        // First apply any bracket subscripts
43073        let mut result = if let Some(expr) = this {
43074            if self.match_token(TokenType::LBracket) {
43075                let index = self.parse_disjunction()?;
43076                self.match_token(TokenType::RBracket);
43077                if let Some(idx) = index {
43078                    Some(Expression::Subscript(Box::new(Subscript {
43079                        this: expr,
43080                        index: idx,
43081                    })))
43082                } else {
43083                    Some(expr)
43084                }
43085            } else {
43086                Some(expr)
43087            }
43088        } else {
43089            None
43090        };
43091
43092        // Handle DOT for qualified column names: table.column or schema.table.column
43093        while self.match_token(TokenType::Dot) {
43094            if result.is_none() {
43095                break;
43096            }
43097            // Handle .* (qualified star) with modifiers
43098            if self.match_token(TokenType::Star) {
43099                // Determine table name from the expression
43100                let table_name = match &result {
43101                    Some(Expression::Column(col)) if col.table.is_none() => Some(col.name.clone()),
43102                    Some(Expression::Dot(dot)) => {
43103                        // For deep qualified names like schema.table.*, use the whole expression name
43104                        fn dot_to_name(expr: &Expression) -> String {
43105                            match expr {
43106                                Expression::Column(col) => {
43107                                    if let Some(ref table) = col.table {
43108                                        format!("{}.{}", table.name, col.name.name)
43109                                    } else {
43110                                        col.name.name.clone()
43111                                    }
43112                                }
43113                                Expression::Dot(d) => {
43114                                    format!("{}.{}", dot_to_name(&d.this), d.field.name)
43115                                }
43116                                _ => String::new(),
43117                            }
43118                        }
43119                        Some(Identifier::new(dot_to_name(&Expression::Dot(dot.clone()))))
43120                    }
43121                    _ => None,
43122                };
43123                let star = self.parse_star_modifiers(table_name)?;
43124                result = Some(Expression::Star(star));
43125                break;
43126            }
43127            // Parse the field identifier - use is_identifier_or_keyword_token to allow keywords
43128            // like "schema" as field names in dot access
43129            // ClickHouse: also allow numeric tuple index access like expr.1, expr.2
43130            if self.is_identifier_or_keyword_token()
43131                || self.check(TokenType::QuotedIdentifier)
43132                || (matches!(
43133                    self.config.dialect,
43134                    Some(crate::dialects::DialectType::ClickHouse)
43135                ) && self.check(TokenType::Number))
43136            {
43137                let token = self.advance();
43138                let field_ident = Identifier {
43139                    name: token.text,
43140                    quoted: token.token_type == TokenType::QuotedIdentifier,
43141                    trailing_comments: Vec::new(),
43142                    span: None,
43143                };
43144                result = Some(Expression::Dot(Box::new(DotAccess {
43145                    this: result.take().unwrap(),
43146                    field: field_ident,
43147                })));
43148            } else {
43149                break;
43150            }
43151        }
43152
43153        // Handle EXCLAMATION for Snowflake model attribute syntax: model!PREDICT(...)
43154        if self.match_token(TokenType::Exclamation) {
43155            if let Some(expr) = result.take() {
43156                // Parse the attribute/function after the exclamation mark
43157                // This can be either a simple identifier (model!admin) or a function call (model!PREDICT(1))
43158                let attr = self.parse_unary()?;
43159                result = Some(Expression::ModelAttribute(Box::new(ModelAttribute {
43160                    this: Box::new(expr),
43161                    expression: Box::new(attr),
43162                })));
43163            }
43164        }
43165
43166        // Handle DCOLON for casts (PostgreSQL syntax: column::type)
43167        if self.match_token(TokenType::DColon) {
43168            if let Some(type_expr) = self.parse_types()? {
43169                if let Some(expr) = result {
43170                    // Extract DataType from the expression
43171                    let data_type = match type_expr {
43172                        Expression::DataType(dt) => dt,
43173                        _ => {
43174                            result = Some(expr);
43175                            return Ok(result);
43176                        }
43177                    };
43178                    result = Some(Expression::Cast(Box::new(Cast {
43179                        this: expr,
43180                        to: data_type,
43181                        trailing_comments: Vec::new(),
43182                        double_colon_syntax: true,
43183                        format: None,
43184                        default: None,
43185                        inferred_type: None,
43186                    })));
43187                }
43188            }
43189        }
43190
43191        // Teradata: (FORMAT '...') phrase after a column/expression
43192        if matches!(
43193            self.config.dialect,
43194            Some(crate::dialects::DialectType::Teradata)
43195        ) && self.check(TokenType::LParen)
43196            && self.check_next(TokenType::Format)
43197        {
43198            self.skip(); // consume (
43199            self.skip(); // consume FORMAT
43200            let format = self.expect_string()?;
43201            self.expect(TokenType::RParen)?;
43202            if let Some(expr) = result.take() {
43203                result = Some(Expression::FormatPhrase(Box::new(FormatPhrase {
43204                    this: Box::new(expr),
43205                    format,
43206                })));
43207            }
43208        }
43209
43210        Ok(result)
43211    }
43212
43213    /// parse_column_reference - Parse column reference (field -> Column)
43214    /// Python: this = self._parse_field(); if isinstance(this, exp.Identifier): return exp.Column(this=this)
43215    pub fn parse_column_reference(&mut self) -> Result<Option<Expression>> {
43216        // Parse the field (identifier or literal)
43217        if let Some(field) = self.parse_field()? {
43218            // If it's an identifier, wrap it in a Column expression
43219            match &field {
43220                Expression::Identifier(id) => {
43221                    return Ok(Some(Expression::boxed_column(Column {
43222                        name: id.clone(),
43223                        table: None,
43224                        join_mark: false,
43225                        trailing_comments: Vec::new(),
43226                        span: None,
43227                        inferred_type: None,
43228                    })));
43229                }
43230                // If it's already something else (like a literal), return as-is
43231                _ => return Ok(Some(field)),
43232            }
43233        }
43234        Ok(None)
43235    }
43236
43237    /// parse_command - Parses a generic SQL command
43238    /// Python: _parse_command
43239    /// Used for commands that we don't have specific parsing for
43240    pub fn parse_command(&mut self) -> Result<Option<Expression>> {
43241        // Get the command keyword from the previous token
43242        let command_text = self.previous().text.to_ascii_uppercase();
43243
43244        // Collect remaining tokens as the command expression (until statement end)
43245        // Use (text, token_type) tuples for smart spacing with join_command_tokens
43246        let mut tokens: Vec<(String, TokenType)> = vec![(command_text, TokenType::Var)];
43247        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
43248            let token = self.advance();
43249            // Preserve quotes for quoted identifiers and strings
43250            let text = if token.token_type == TokenType::QuotedIdentifier {
43251                // Re-add the identifier quote characters
43252                // Use backticks as default; this handles MySQL backtick-quoted identifiers
43253                // and double-quoted identifiers for other dialects
43254                let quote_char = if self.config.dialect == Some(crate::dialects::DialectType::MySQL)
43255                    || self.config.dialect == Some(crate::dialects::DialectType::SingleStore)
43256                    || self.config.dialect == Some(crate::dialects::DialectType::Doris)
43257                    || self.config.dialect == Some(crate::dialects::DialectType::StarRocks)
43258                {
43259                    '`'
43260                } else {
43261                    '"'
43262                };
43263                format!("{}{}{}", quote_char, token.text, quote_char)
43264            } else if token.token_type == TokenType::String {
43265                format!("'{}'", token.text)
43266            } else {
43267                token.text.clone()
43268            };
43269            tokens.push((text, token.token_type));
43270        }
43271
43272        Ok(Some(Expression::Command(Box::new(Command {
43273            this: self.join_command_tokens(tokens),
43274        }))))
43275    }
43276
43277    /// parse_commit_or_rollback - Implemented from Python _parse_commit_or_rollback
43278    #[allow(unused_variables, unused_mut)]
43279    pub fn parse_commit_or_rollback(&mut self) -> Result<Option<Expression>> {
43280        if self.match_text_seq(&["TO"]) {
43281            return Ok(Some(Expression::Rollback(Box::new(Rollback {
43282                savepoint: None,
43283                this: None,
43284            }))));
43285        }
43286        if self.match_text_seq(&["SAVEPOINT"]) {
43287            // Matched: SAVEPOINT
43288            return Ok(None);
43289        }
43290        Ok(None)
43291    }
43292
43293    /// parse_composite_key_property - Implemented from Python _parse_composite_key_property
43294    #[allow(unused_variables, unused_mut)]
43295    pub fn parse_composite_key_property(&mut self) -> Result<Option<Expression>> {
43296        if self.match_text_seq(&["KEY"]) {
43297            // Matched: KEY
43298            return Ok(None);
43299        }
43300        Ok(None)
43301    }
43302
43303    /// parse_comprehension - Implemented from Python _parse_comprehension
43304    /// Parses list comprehension: expr FOR var [, position] IN iterator [IF condition]
43305    pub fn parse_comprehension(&mut self, this: Option<Expression>) -> Result<Option<Expression>> {
43306        let start_index = self.current;
43307
43308        // Parse expression (column)
43309        let expression = self.parse_column()?;
43310
43311        // Parse optional position (if comma follows)
43312        let position = if self.match_token(TokenType::Comma) {
43313            self.parse_column()?.map(Box::new)
43314        } else {
43315            None
43316        };
43317
43318        // Must have IN keyword
43319        if !self.match_token(TokenType::In) {
43320            // Backtrack
43321            self.current = start_index.saturating_sub(1);
43322            return Ok(None);
43323        }
43324
43325        // Parse iterator
43326        let iterator = self.parse_column()?.map(Box::new);
43327
43328        // Parse optional condition (IF followed by expression)
43329        let condition = if self.match_text_seq(&["IF"]) {
43330            self.parse_disjunction()?.map(Box::new)
43331        } else {
43332            None
43333        };
43334
43335        // Build the comprehension expression
43336        match (this, expression) {
43337            (Some(t), Some(e)) => Ok(Some(Expression::Comprehension(Box::new(Comprehension {
43338                this: Box::new(t),
43339                expression: Box::new(e),
43340                position,
43341                iterator,
43342                condition,
43343            })))),
43344            _ => Ok(None),
43345        }
43346    }
43347
43348    /// parse_compress - Parses COMPRESS column constraint (Teradata)
43349    /// Python: _parse_compress
43350    /// Format: COMPRESS or COMPRESS (value1, value2, ...)
43351    pub fn parse_compress(&mut self) -> Result<Option<Expression>> {
43352        // Check if it's a parenthesized list of values
43353        if self.check(TokenType::LParen) {
43354            // Parse wrapped CSV of bitwise expressions
43355            self.skip(); // consume LParen
43356            let mut expressions = Vec::new();
43357            loop {
43358                if let Some(expr) = self.parse_bitwise()? {
43359                    expressions.push(expr);
43360                } else {
43361                    break;
43362                }
43363                if !self.match_token(TokenType::Comma) {
43364                    break;
43365                }
43366            }
43367            self.expect(TokenType::RParen)?;
43368
43369            // Wrap in a Tuple if multiple values
43370            let this = if expressions.len() == 1 {
43371                Some(Box::new(expressions.into_iter().next().unwrap()))
43372            } else if expressions.is_empty() {
43373                None
43374            } else {
43375                Some(Box::new(Expression::Tuple(Box::new(Tuple { expressions }))))
43376            };
43377
43378            Ok(Some(Expression::CompressColumnConstraint(Box::new(
43379                CompressColumnConstraint { this },
43380            ))))
43381        } else {
43382            // Single value or no value
43383            let this = self.parse_bitwise()?.map(Box::new);
43384            Ok(Some(Expression::CompressColumnConstraint(Box::new(
43385                CompressColumnConstraint { this },
43386            ))))
43387        }
43388    }
43389
43390    /// parse_conjunction - Parses AND expressions
43391    /// Python: _parse_conjunction
43392    /// Delegates to the existing parse_and in the operator precedence chain
43393    pub fn parse_conjunction(&mut self) -> Result<Option<Expression>> {
43394        match self.parse_and() {
43395            Ok(expr) => Ok(Some(expr)),
43396            Err(_) => Ok(None),
43397        }
43398    }
43399
43400    /// parse_connect_with_prior - Parses expression in CONNECT BY context with PRIOR support
43401    /// Python: _parse_connect_with_prior
43402    /// This method temporarily treats PRIOR as a prefix operator while parsing the expression
43403    pub fn parse_connect_with_prior(&mut self) -> Result<Option<Expression>> {
43404        // parse_connect_expression already handles PRIOR as a prefix operator
43405        let connect = self.parse_connect_expression()?;
43406        Ok(Some(connect))
43407    }
43408
43409    /// parse_constraint - Parses named or unnamed constraint
43410    /// Python: _parse_constraint
43411    pub fn parse_constraint(&mut self) -> Result<Option<Expression>> {
43412        // Check for CONSTRAINT keyword (named constraint)
43413        if !self.match_token(TokenType::Constraint) {
43414            // Try to parse an unnamed constraint
43415            return self.parse_unnamed_constraint();
43416        }
43417
43418        // Parse the constraint name
43419        let name = self.parse_id_var()?;
43420        if name.is_none() {
43421            return Ok(None);
43422        }
43423
43424        // Parse the constraint expressions (PRIMARY KEY, UNIQUE, FOREIGN KEY, CHECK, etc.)
43425        let expressions = self.parse_unnamed_constraints()?;
43426
43427        Ok(Some(Expression::Constraint(Box::new(Constraint {
43428            this: Box::new(name.unwrap()),
43429            expressions,
43430        }))))
43431    }
43432
43433    /// parse_unnamed_constraints - Parses multiple unnamed constraints
43434    /// Python: _parse_unnamed_constraints
43435    pub fn parse_unnamed_constraints(&mut self) -> Result<Vec<Expression>> {
43436        let mut constraints = Vec::new();
43437
43438        loop {
43439            if let Some(constraint) = self.parse_unnamed_constraint()? {
43440                constraints.push(constraint);
43441            } else {
43442                break;
43443            }
43444        }
43445
43446        Ok(constraints)
43447    }
43448
43449    /// parse_unnamed_constraint - Parses a single unnamed constraint
43450    /// Python: _parse_unnamed_constraint
43451    pub fn parse_unnamed_constraint(&mut self) -> Result<Option<Expression>> {
43452        // Try PRIMARY KEY
43453        if self.match_text_seq(&["PRIMARY", "KEY"]) {
43454            // ClickHouse: PRIMARY KEY expr (without parens) in schema = table-level PK expression
43455            if matches!(
43456                self.config.dialect,
43457                Some(crate::dialects::DialectType::ClickHouse)
43458            ) && !self.check(TokenType::LParen)
43459            {
43460                let expr = self.parse_expression()?;
43461                return Ok(Some(Expression::Raw(Raw {
43462                    sql: format!("PRIMARY KEY {}", expr),
43463                })));
43464            }
43465            return self.parse_primary_key();
43466        }
43467
43468        // Try UNIQUE
43469        if self.match_texts(&["UNIQUE"]) {
43470            return self.parse_unique();
43471        }
43472
43473        // Try FOREIGN KEY
43474        if self.match_text_seq(&["FOREIGN", "KEY"]) {
43475            return self.parse_foreign_key();
43476        }
43477
43478        // Try CHECK
43479        if self.match_texts(&["CHECK"]) {
43480            let expr = self.parse_wrapped()?;
43481            if let Some(check_expr) = expr {
43482                return Ok(Some(Expression::CheckColumnConstraint(Box::new(
43483                    CheckColumnConstraint {
43484                        this: Box::new(check_expr),
43485                        enforced: None,
43486                    },
43487                ))));
43488            }
43489        }
43490
43491        // Try NOT NULL
43492        if self.match_text_seq(&["NOT", "NULL"]) {
43493            return Ok(Some(Expression::NotNullColumnConstraint(Box::new(
43494                NotNullColumnConstraint {
43495                    allow_null: None, // NOT NULL means allow_null is not set
43496                },
43497            ))));
43498        }
43499
43500        // Try NULL (allow null)
43501        if self.match_texts(&["NULL"]) {
43502            return Ok(Some(Expression::NotNullColumnConstraint(Box::new(
43503                NotNullColumnConstraint {
43504                    allow_null: Some(Box::new(Expression::Boolean(BooleanLiteral {
43505                        value: true,
43506                    }))),
43507                },
43508            ))));
43509        }
43510
43511        // Try DEFAULT
43512        if self.match_token(TokenType::Default) {
43513            let default_value = self.parse_bitwise()?;
43514            if let Some(val) = default_value {
43515                return Ok(Some(Expression::DefaultColumnConstraint(Box::new(
43516                    DefaultColumnConstraint {
43517                        this: Box::new(val),
43518                    },
43519                ))));
43520            }
43521        }
43522
43523        // Try REFERENCES (inline foreign key)
43524        if self.match_texts(&["REFERENCES"]) {
43525            return self.parse_references();
43526        }
43527
43528        // ClickHouse: INDEX name expr TYPE type_name [GRANULARITY n]
43529        if matches!(
43530            self.config.dialect,
43531            Some(crate::dialects::DialectType::ClickHouse)
43532        ) && self.match_token(TokenType::Index)
43533        {
43534            let name = self.expect_identifier_or_keyword_with_quoted()?;
43535            // Use parse_conjunction to handle comparisons like c0 < (SELECT _table)
43536            let expression = self.parse_conjunction()?.ok_or_else(|| {
43537                self.parse_error("Expected expression in ClickHouse INDEX definition")
43538            })?;
43539            let index_type = if self.match_token(TokenType::Type) {
43540                if let Some(func) = self.parse_function()? {
43541                    Some(Box::new(func))
43542                } else if !self.is_at_end() {
43543                    let type_name = self.advance().text.clone();
43544                    if self.check(TokenType::LParen) {
43545                        self.skip();
43546                        let mut args = Vec::new();
43547                        if !self.check(TokenType::RParen) {
43548                            args.push(self.parse_expression()?);
43549                            while self.match_token(TokenType::Comma) {
43550                                args.push(self.parse_expression()?);
43551                            }
43552                        }
43553                        self.expect(TokenType::RParen)?;
43554                        Some(Box::new(Expression::Function(Box::new(Function::new(
43555                            type_name, args,
43556                        )))))
43557                    } else {
43558                        Some(Box::new(Expression::Identifier(Identifier::new(type_name))))
43559                    }
43560                } else {
43561                    None
43562                }
43563            } else {
43564                None
43565            };
43566            let _granularity = if self.match_identifier("GRANULARITY") {
43567                let _ = self.parse_expression()?;
43568                true
43569            } else {
43570                false
43571            };
43572            // Return as a raw SQL expression preserving the INDEX definition
43573            let mut sql = format!("INDEX {} ", name.name);
43574            if let Some(ref idx_type) = index_type {
43575                sql.push_str(&format!("{} TYPE {} ", expression, idx_type));
43576            }
43577            return Ok(Some(Expression::Raw(Raw {
43578                sql: sql.trim().to_string(),
43579            })));
43580        }
43581
43582        // ClickHouse: PROJECTION name (SELECT ...) or PROJECTION name INDEX expr TYPE type_name
43583        if matches!(
43584            self.config.dialect,
43585            Some(crate::dialects::DialectType::ClickHouse)
43586        ) && self.check_identifier("PROJECTION")
43587        {
43588            self.skip(); // consume PROJECTION
43589            let name = self.expect_identifier_or_keyword_with_quoted()?;
43590            // Parse the projection body - either (SELECT ...) or INDEX expr TYPE type_name
43591            if self.match_token(TokenType::LParen) {
43592                let mut depth = 1i32;
43593                let start = self.current;
43594                while !self.is_at_end() && depth > 0 {
43595                    if self.check(TokenType::LParen) {
43596                        depth += 1;
43597                    }
43598                    if self.check(TokenType::RParen) {
43599                        depth -= 1;
43600                        if depth == 0 {
43601                            break;
43602                        }
43603                    }
43604                    self.skip();
43605                }
43606                let body_sql = self.tokens_to_sql(start, self.current);
43607                self.expect(TokenType::RParen)?;
43608                return Ok(Some(Expression::Raw(Raw {
43609                    sql: format!("PROJECTION {} ({})", name.name, body_sql),
43610                })));
43611            }
43612            // PROJECTION name INDEX expr TYPE type_name
43613            if self.match_token(TokenType::Index) {
43614                let expr = self.parse_bitwise()?.ok_or_else(|| {
43615                    self.parse_error(
43616                        "Expected expression in ClickHouse PROJECTION INDEX definition",
43617                    )
43618                })?;
43619                let type_str = if self.match_token(TokenType::Type) {
43620                    if !self.is_at_end() {
43621                        let t = self.advance().text.clone();
43622                        format!(" TYPE {}", t)
43623                    } else {
43624                        String::new()
43625                    }
43626                } else {
43627                    String::new()
43628                };
43629                return Ok(Some(Expression::Raw(Raw {
43630                    sql: format!("PROJECTION {} INDEX {}{}", name.name, expr, type_str),
43631                })));
43632            }
43633            return Ok(Some(Expression::Raw(Raw {
43634                sql: format!("PROJECTION {}", name.name),
43635            })));
43636        }
43637
43638        Ok(None)
43639    }
43640
43641    /// parse_contains_property - Implemented from Python _parse_contains_property
43642    #[allow(unused_variables, unused_mut)]
43643    pub fn parse_contains_property(&mut self) -> Result<Option<Expression>> {
43644        if self.match_text_seq(&["SQL"]) {
43645            // Matched: SQL
43646            return Ok(None);
43647        }
43648        Ok(None)
43649    }
43650
43651    /// parse_convert - Ported from Python _parse_convert
43652    /// Parses CONVERT function: CONVERT(expr USING charset) or CONVERT(expr, type)
43653    #[allow(unused_variables, unused_mut)]
43654    pub fn parse_convert(&mut self) -> Result<Option<Expression>> {
43655        // Parse the expression to convert
43656        let this = match self.parse_bitwise() {
43657            Ok(Some(expr)) => expr,
43658            Ok(None) => return Ok(None),
43659            Err(e) => return Err(e),
43660        };
43661
43662        // Check for USING charset (CONVERT(x USING utf8))
43663        if self.match_token(TokenType::Using) {
43664            let _ = self.parse_var(); // charset
43665                                      // Return as Cast with charset
43666            return Ok(Some(Expression::Cast(Box::new(Cast {
43667                this,
43668                to: DataType::Char { length: None },
43669                trailing_comments: Vec::new(),
43670                double_colon_syntax: false,
43671                format: None,
43672                default: None,
43673                inferred_type: None,
43674            }))));
43675        }
43676
43677        // Check for comma then type (CONVERT(x, INT))
43678        if self.match_token(TokenType::Comma) {
43679            let data_type = self.parse_data_type()?;
43680            return Ok(Some(Expression::Cast(Box::new(Cast {
43681                this,
43682                to: data_type,
43683                trailing_comments: Vec::new(),
43684                double_colon_syntax: false,
43685                format: None,
43686                default: None,
43687                inferred_type: None,
43688            }))));
43689        }
43690
43691        // No type specified, return as-is wrapped in Cast
43692        Ok(Some(Expression::Cast(Box::new(Cast {
43693            this,
43694            to: DataType::Char { length: None },
43695            trailing_comments: Vec::new(),
43696            double_colon_syntax: false,
43697            format: None,
43698            default: None,
43699            inferred_type: None,
43700        }))))
43701    }
43702
43703    /// parse_copy_parameters - Implemented from Python _parse_copy_parameters
43704    /// parse_copy_parameters - Parses COPY statement parameters
43705    /// Returns a tuple of CopyParameter expressions
43706    pub fn parse_copy_parameters(&mut self) -> Result<Option<Expression>> {
43707        let mut options = Vec::new();
43708
43709        while !self.is_at_end() && !self.check(TokenType::RParen) {
43710            // Parse option name as var
43711            let option = self.parse_var()?;
43712            if option.is_none() {
43713                break;
43714            }
43715
43716            let option_name = match &option {
43717                Some(Expression::Var(v)) => v.this.to_ascii_uppercase(),
43718                Some(Expression::Identifier(id)) => id.name.to_ascii_uppercase(),
43719                _ => String::new(),
43720            };
43721
43722            // Options and values may be separated by whitespace, "=" or "AS"
43723            self.match_token(TokenType::Eq);
43724            self.match_token(TokenType::Alias);
43725
43726            // Parse value based on option type
43727            let (expression, expressions) = if (option_name == "FILE_FORMAT"
43728                || option_name == "FORMAT_OPTIONS")
43729                && self.check(TokenType::LParen)
43730            {
43731                // Parse wrapped options for FILE_FORMAT
43732                let wrapped = self.parse_wrapped_options()?;
43733                let exprs = match wrapped {
43734                    Some(Expression::Tuple(t)) => t.expressions,
43735                    Some(e) => vec![e],
43736                    None => Vec::new(),
43737                };
43738                (None, exprs)
43739            } else if option_name == "FILE_FORMAT" {
43740                // T-SQL external file format case
43741                let field = self.parse_field()?;
43742                (field, Vec::new())
43743            } else if option_name == "FORMAT"
43744                && self.previous().token_type == TokenType::Alias
43745                && self.match_texts(&["AVRO", "JSON"])
43746            {
43747                // FORMAT AS AVRO/JSON
43748                let format_type = self.previous().text.to_ascii_uppercase();
43749                let field = self.parse_field()?;
43750                (
43751                    Some(Expression::Var(Box::new(Var {
43752                        this: format!("FORMAT AS {}", format_type),
43753                    }))),
43754                    field.map_or(Vec::new(), |f| vec![f]),
43755                )
43756            } else {
43757                // Parse unquoted field or bracket
43758                let expr = self
43759                    .parse_unquoted_field()?
43760                    .or_else(|| self.parse_bracket().ok().flatten());
43761                (expr, Vec::new())
43762            };
43763
43764            options.push(Expression::CopyParameter(Box::new(CopyParameter {
43765                name: option_name,
43766                value: expression,
43767                values: expressions,
43768                eq: true,
43769            })));
43770
43771            // Optional comma separator (dialect-specific)
43772            self.match_token(TokenType::Comma);
43773        }
43774
43775        if options.is_empty() {
43776            Ok(None)
43777        } else {
43778            Ok(Some(Expression::Tuple(Box::new(Tuple {
43779                expressions: options,
43780            }))))
43781        }
43782    }
43783
43784    /// parse_copy_property - Implemented from Python _parse_copy_property
43785    #[allow(unused_variables, unused_mut)]
43786    pub fn parse_copy_property(&mut self) -> Result<Option<Expression>> {
43787        if self.match_text_seq(&["GRANTS"]) {
43788            // Matched: GRANTS
43789            return Ok(None);
43790        }
43791        Ok(None)
43792    }
43793
43794    /// parse_create_like - Implemented from Python _parse_create_like
43795    /// Calls: parse_id_var
43796    #[allow(unused_variables, unused_mut)]
43797    pub fn parse_create_like(&mut self) -> Result<Option<Expression>> {
43798        if self.match_texts(&["INCLUDING", "EXCLUDING"]) {
43799            // Matched one of: INCLUDING, EXCLUDING
43800            return Ok(None);
43801        }
43802        Ok(None)
43803    }
43804
43805    /// parse_credentials - Implemented from Python _parse_credentials
43806    #[allow(unused_variables, unused_mut)]
43807    pub fn parse_credentials(&mut self) -> Result<Option<Expression>> {
43808        if self.match_text_seq(&["STORAGE_INTEGRATION", "="]) {
43809            return Ok(Some(Expression::Credentials(Box::new(Credentials {
43810                credentials: Vec::new(),
43811                encryption: None,
43812                storage: None,
43813            }))));
43814        }
43815        if self.match_text_seq(&["CREDENTIALS"]) {
43816            // Matched: CREDENTIALS
43817            return Ok(None);
43818        }
43819        Ok(None)
43820    }
43821
43822    /// parse_csv - Parses comma-separated expressions
43823    /// Python: _parse_csv
43824    /// In Python this takes a parse_method callback, but in Rust we use parse_expression_list
43825    pub fn parse_csv(&mut self) -> Result<Option<Expression>> {
43826        let expressions = self.parse_expression_list()?;
43827        if expressions.is_empty() {
43828            return Ok(None);
43829        }
43830        Ok(Some(Expression::Tuple(Box::new(Tuple { expressions }))))
43831    }
43832
43833    /// parse_cte - Implemented from Python _parse_cte
43834    /// Calls: parse_wrapped_id_vars
43835    #[allow(unused_variables, unused_mut)]
43836    pub fn parse_cte(&mut self) -> Result<Option<Expression>> {
43837        if self.match_text_seq(&["USING", "KEY"]) {
43838            return Ok(Some(Expression::Values(Box::new(Values {
43839                expressions: Vec::new(),
43840                alias: None,
43841                column_aliases: Vec::new(),
43842            }))));
43843        }
43844        if self.match_text_seq(&["NOT", "MATERIALIZED"]) {
43845            // Matched: NOT MATERIALIZED
43846            return Ok(None);
43847        }
43848        if self.match_text_seq(&["MATERIALIZED"]) {
43849            // Matched: MATERIALIZED
43850            return Ok(None);
43851        }
43852        Ok(None)
43853    }
43854
43855    /// parse_cube_or_rollup - Ported from Python _parse_cube_or_rollup
43856    /// Parses CUBE(...) or ROLLUP(...) expressions in GROUP BY
43857    #[allow(unused_variables, unused_mut)]
43858    pub fn parse_cube_or_rollup(&mut self) -> Result<Option<Expression>> {
43859        // Check for CUBE or ROLLUP keyword
43860        let is_cube = self.match_texts(&["CUBE"]);
43861        let is_rollup = if !is_cube {
43862            self.match_texts(&["ROLLUP"])
43863        } else {
43864            false
43865        };
43866
43867        if !is_cube && !is_rollup {
43868            return Ok(None);
43869        }
43870
43871        // Parse wrapped expressions
43872        self.expect(TokenType::LParen)?;
43873        let mut expressions = Vec::new();
43874        if !self.check(TokenType::RParen) {
43875            loop {
43876                match self.parse_bitwise() {
43877                    Ok(Some(expr)) => expressions.push(expr),
43878                    Ok(None) => break,
43879                    Err(e) => return Err(e),
43880                }
43881                if !self.match_token(TokenType::Comma) {
43882                    break;
43883                }
43884            }
43885        }
43886        self.expect(TokenType::RParen)?;
43887
43888        if is_cube {
43889            Ok(Some(Expression::Cube(Box::new(Cube { expressions }))))
43890        } else {
43891            Ok(Some(Expression::Rollup(Box::new(Rollup { expressions }))))
43892        }
43893    }
43894
43895    /// parse_data_deletion_property - Implemented from Python _parse_data_deletion_property
43896    /// Calls: parse_column, parse_retention_period
43897    #[allow(unused_variables, unused_mut)]
43898    pub fn parse_data_deletion_property(&mut self) -> Result<Option<Expression>> {
43899        if self.match_text_seq(&["ON"]) {
43900            // Matched: ON
43901            return Ok(None);
43902        }
43903        if self.match_text_seq(&["OFF"]) {
43904            // Matched: OFF
43905            return Ok(None);
43906        }
43907        if self.match_text_seq(&["FILTER_COLUMN", "="]) {
43908            // Matched: FILTER_COLUMN =
43909            return Ok(None);
43910        }
43911        Ok(None)
43912    }
43913
43914    /// parse_datablocksize - Implemented from Python _parse_datablocksize
43915    /// Calls: parse_number
43916    #[allow(unused_variables, unused_mut)]
43917    pub fn parse_datablocksize(&mut self) -> Result<Option<Expression>> {
43918        if self.match_texts(&["BYTES", "KBYTES", "KILOBYTES"]) {
43919            // Matched one of: BYTES, KBYTES, KILOBYTES
43920            return Ok(None);
43921        }
43922        Ok(None)
43923    }
43924
43925    /// parse_dcolon - Delegates to parse_types
43926    #[allow(unused_variables, unused_mut)]
43927    pub fn parse_dcolon(&mut self) -> Result<Option<Expression>> {
43928        self.parse_types()
43929    }
43930
43931    /// parse_ddl_select - Ported from Python _parse_ddl_select
43932    /// Parses a SELECT statement in DDL context (CREATE TABLE AS SELECT, INSERT INTO ... SELECT)
43933    #[allow(unused_variables, unused_mut)]
43934    pub fn parse_ddl_select(&mut self) -> Result<Option<Expression>> {
43935        // Parse a nested SELECT statement
43936        let select = self.parse_select_query()?;
43937
43938        if select.is_none() {
43939            return Ok(None);
43940        }
43941
43942        // Apply set operations (UNION, INTERSECT, EXCEPT)
43943        let with_set_ops = self.parse_set_operations_with_expr(select)?;
43944
43945        // Return the result (query modifiers would be applied by parse_select_query already)
43946        Ok(with_set_ops)
43947    }
43948
43949    /// parse_for_in - BigQuery procedural FOR...IN...DO loop
43950    /// Python: BigQuery._parse_for_in
43951    /// Format: FOR variable IN (query) DO statement(s) END FOR
43952    /// Example: FOR record IN (SELECT * FROM t) DO SELECT record.col
43953    pub fn parse_for_in(&mut self) -> Result<Expression> {
43954        // Parse: variable IN (query)
43955        // This is handled by parse_range which produces an In expression
43956        let this = self
43957            .parse_range()?
43958            .ok_or_else(|| self.parse_error("Expected expression after FOR"))?;
43959
43960        // Match DO keyword
43961        self.match_text_seq(&["DO"]);
43962
43963        // Parse the body statement
43964        let expression = self.parse_statement()?;
43965
43966        Ok(Expression::ForIn(Box::new(ForIn {
43967            this: Box::new(this),
43968            expression: Box::new(expression),
43969        })))
43970    }
43971
43972    /// parse_declare - Parses DECLARE statement
43973    /// Python: _parse_declare
43974    /// Format: DECLARE var1 type [DEFAULT expr], var2 type [DEFAULT expr], ...
43975    pub fn parse_declare(&mut self) -> Result<Option<Expression>> {
43976        // Try to parse comma-separated declare items
43977        let mut expressions = Vec::new();
43978
43979        // BigQuery multi-variable DECLARE: DECLARE X, Y, Z INT64 [DEFAULT expr]
43980        // Detect by looking ahead: if we see identifier, comma, identifier pattern
43981        // before a data type keyword, collect all names then parse type once.
43982        let saved = self.current;
43983        let mut multi_names: Vec<Expression> = Vec::new();
43984        if let Some(first_var) = self.parse_id_var()? {
43985            // Check if next is a comma (BigQuery multi-var syntax)
43986            if self.check(TokenType::Comma) && !self.check_identifier("CURSOR") {
43987                // Speculatively collect comma-separated identifiers
43988                multi_names.push(first_var);
43989                while self.match_token(TokenType::Comma) {
43990                    if let Some(next_var) = self.parse_id_var()? {
43991                        multi_names.push(next_var);
43992                    } else {
43993                        break;
43994                    }
43995                }
43996                // Now check if we're at a data type (not comma, not @, not semicolon)
43997                // If so, this is BigQuery multi-var syntax
43998                if multi_names.len() > 1 && !self.is_at_end() && !self.check(TokenType::Semicolon) {
43999                    let data_type = self.parse_data_type()?;
44000                    let kind_str = self.data_type_to_sql(&data_type);
44001                    let default = if self.match_token(TokenType::Default)
44002                        || self.match_token(TokenType::Eq)
44003                    {
44004                        Some(Box::new(self.parse_expression()?))
44005                    } else {
44006                        None
44007                    };
44008                    let first_name = multi_names.remove(0);
44009                    expressions.push(Expression::DeclareItem(Box::new(DeclareItem {
44010                        this: Box::new(first_name),
44011                        kind: Some(kind_str),
44012                        default,
44013                        has_as: false,
44014                        additional_names: multi_names,
44015                    })));
44016                    return Ok(Some(Expression::Declare(Box::new(Declare { expressions }))));
44017                }
44018            }
44019        }
44020        // Reset and parse normally
44021        self.current = saved;
44022
44023        loop {
44024            if let Some(item) = self.parse_declareitem()? {
44025                expressions.push(item);
44026            } else {
44027                break;
44028            }
44029            if !self.match_token(TokenType::Comma) {
44030                break;
44031            }
44032        }
44033
44034        // If we successfully parsed at least one item, return the Declare
44035        if !expressions.is_empty() {
44036            return Ok(Some(Expression::Declare(Box::new(Declare { expressions }))));
44037        }
44038
44039        Ok(None)
44040    }
44041
44042    /// parse_declareitem - Parse a DECLARE item (variable declaration)
44043    /// TSQL format: @var AS type [= expr] or @var type [= expr]
44044    /// Also handles: DECLARE name CURSOR FOR SELECT ...
44045    /// Also handles: DECLARE @var TABLE (col_defs)
44046    #[allow(unused_variables, unused_mut)]
44047    pub fn parse_declareitem(&mut self) -> Result<Option<Expression>> {
44048        // Consume optional VAR or VARIABLE keyword (Spark/Databricks)
44049        if self.check_identifier("VAR") || self.check_identifier("VARIABLE") {
44050            self.skip();
44051        }
44052
44053        // Parse the variable name (starts with @ or is a cursor name)
44054        let var = if let Some(v) = self.parse_id_var()? {
44055            v
44056        } else {
44057            return Ok(None);
44058        };
44059
44060        // Check for CURSOR FOR syntax: DECLARE name CURSOR FOR SELECT ...
44061        if self.check_identifier("CURSOR") {
44062            self.skip(); // consume CURSOR
44063                            // Parse optional cursor options before FOR (e.g., SCROLL, INSENSITIVE, etc.)
44064                            // For now just look for FOR
44065            if self.match_token(TokenType::For) {
44066                // Capture the remaining tokens as the cursor query using tokens_to_sql for proper spacing
44067                let start = self.current;
44068                while !self.is_at_end() && !self.check(TokenType::Semicolon) {
44069                    self.skip();
44070                }
44071                let query_str = self.tokens_to_sql_uppercased(start, self.current);
44072                let kind_str = format!("CURSOR FOR {}", query_str);
44073                return Ok(Some(Expression::DeclareItem(Box::new(DeclareItem {
44074                    this: Box::new(var),
44075                    kind: Some(kind_str),
44076                    default: None,
44077                    has_as: false,
44078                    additional_names: Vec::new(),
44079                }))));
44080            } else {
44081                return Ok(Some(Expression::DeclareItem(Box::new(DeclareItem {
44082                    this: Box::new(var),
44083                    kind: Some("CURSOR".to_string()),
44084                    default: None,
44085                    has_as: false,
44086                    additional_names: Vec::new(),
44087                }))));
44088            }
44089        }
44090
44091        // Parse optional AS keyword
44092        let has_as = self.match_token(TokenType::As);
44093
44094        // Check for TABLE type with column definitions
44095        if self.check(TokenType::Table) {
44096            self.skip(); // consume TABLE
44097            if self.match_token(TokenType::LParen) {
44098                // Parse the TABLE column definitions using tokens_to_sql for proper spacing
44099                let start = self.current;
44100                let mut depth = 1;
44101                while depth > 0 && !self.is_at_end() {
44102                    if self.check(TokenType::LParen) {
44103                        depth += 1;
44104                    }
44105                    if self.check(TokenType::RParen) {
44106                        depth -= 1;
44107                        if depth == 0 {
44108                            break;
44109                        }
44110                    }
44111                    self.skip();
44112                }
44113                let col_defs_str = self.tokens_to_sql_uppercased(start, self.current);
44114                self.expect(TokenType::RParen)?;
44115                let kind_str = format!("TABLE ({})", col_defs_str);
44116                return Ok(Some(Expression::DeclareItem(Box::new(DeclareItem {
44117                    this: Box::new(var),
44118                    kind: Some(kind_str),
44119                    default: None,
44120                    has_as,
44121                    additional_names: Vec::new(),
44122                }))));
44123            } else {
44124                return Ok(Some(Expression::DeclareItem(Box::new(DeclareItem {
44125                    this: Box::new(var),
44126                    kind: Some("TABLE".to_string()),
44127                    default: None,
44128                    has_as,
44129                    additional_names: Vec::new(),
44130                }))));
44131            }
44132        }
44133
44134        // Parse the data type
44135        let data_type = self.parse_data_type()?;
44136        let kind_str = self.data_type_to_sql(&data_type);
44137
44138        // Parse optional DEFAULT value or = value (TSQL uses =)
44139        let default = if self.match_token(TokenType::Default) || self.match_token(TokenType::Eq) {
44140            Some(Box::new(self.parse_expression()?))
44141        } else {
44142            None
44143        };
44144
44145        Ok(Some(Expression::DeclareItem(Box::new(DeclareItem {
44146            this: Box::new(var),
44147            kind: Some(kind_str),
44148            default,
44149            has_as,
44150            additional_names: Vec::new(),
44151        }))))
44152    }
44153
44154    /// Convert a DataType to its SQL string representation
44155    fn data_type_to_sql(&self, dt: &DataType) -> String {
44156        match dt {
44157            DataType::Boolean => "BOOLEAN".to_string(),
44158            DataType::TinyInt { length } => {
44159                if let Some(n) = length {
44160                    format!("TINYINT({})", n)
44161                } else {
44162                    "TINYINT".to_string()
44163                }
44164            }
44165            DataType::SmallInt { length } => {
44166                if let Some(n) = length {
44167                    format!("SMALLINT({})", n)
44168                } else {
44169                    "SMALLINT".to_string()
44170                }
44171            }
44172            DataType::Int {
44173                length,
44174                integer_spelling,
44175            } => {
44176                if let Some(n) = length {
44177                    if *integer_spelling {
44178                        format!("INTEGER({})", n)
44179                    } else {
44180                        format!("INT({})", n)
44181                    }
44182                } else if *integer_spelling {
44183                    "INTEGER".to_string()
44184                } else {
44185                    "INT".to_string()
44186                }
44187            }
44188            DataType::BigInt { length } => {
44189                if let Some(n) = length {
44190                    format!("BIGINT({})", n)
44191                } else {
44192                    "BIGINT".to_string()
44193                }
44194            }
44195            DataType::Float {
44196                precision, scale, ..
44197            } => match (precision, scale) {
44198                (Some(p), Some(s)) => format!("FLOAT({}, {})", p, s),
44199                (Some(p), None) => format!("FLOAT({})", p),
44200                _ => "FLOAT".to_string(),
44201            },
44202            DataType::Double { precision, scale } => match (precision, scale) {
44203                (Some(p), Some(s)) => format!("DOUBLE({}, {})", p, s),
44204                (Some(p), None) => format!("DOUBLE({})", p),
44205                _ => "DOUBLE".to_string(),
44206            },
44207            DataType::Decimal { precision, scale } => match (precision, scale) {
44208                (Some(p), Some(s)) => format!("DECIMAL({}, {})", p, s),
44209                (Some(p), None) => format!("DECIMAL({})", p),
44210                _ => "DECIMAL".to_string(),
44211            },
44212            DataType::Char { length } => {
44213                if let Some(n) = length {
44214                    format!("CHAR({})", n)
44215                } else {
44216                    "CHAR".to_string()
44217                }
44218            }
44219            DataType::VarChar { length, .. } => {
44220                if let Some(n) = length {
44221                    format!("VARCHAR({})", n)
44222                } else {
44223                    "VARCHAR".to_string()
44224                }
44225            }
44226            DataType::Text => "TEXT".to_string(),
44227            DataType::Date => "DATE".to_string(),
44228            DataType::Time { precision, .. } => {
44229                if let Some(p) = precision {
44230                    format!("TIME({})", p)
44231                } else {
44232                    "TIME".to_string()
44233                }
44234            }
44235            DataType::Timestamp { precision, .. } => {
44236                if let Some(p) = precision {
44237                    format!("TIMESTAMP({})", p)
44238                } else {
44239                    "TIMESTAMP".to_string()
44240                }
44241            }
44242            DataType::Binary { length } => {
44243                if let Some(n) = length {
44244                    format!("BINARY({})", n)
44245                } else {
44246                    "BINARY".to_string()
44247                }
44248            }
44249            DataType::VarBinary { length } => {
44250                if let Some(n) = length {
44251                    format!("VARBINARY({})", n)
44252                } else {
44253                    "VARBINARY".to_string()
44254                }
44255            }
44256            DataType::Blob => "BLOB".to_string(),
44257            DataType::Json => "JSON".to_string(),
44258            DataType::Uuid => "UUID".to_string(),
44259            DataType::Custom { name } => name.clone(), // Custom types (INT64, FLOAT64, etc.)
44260            _ => format!("{:?}", dt),                  // Fallback for unknown types
44261        }
44262    }
44263
44264    /// parse_decode - Ported from Python _parse_decode
44265    /// Parses Oracle-style DECODE or simple DECODE function
44266    /// If 3+ args: Oracle DECODE(expr, search1, result1, ..., default)
44267    /// If 2 args: character set decode (expr, charset)
44268    #[allow(unused_variables, unused_mut)]
44269    pub fn parse_decode(&mut self) -> Result<Option<Expression>> {
44270        // Parse comma-separated arguments
44271        let mut args: Vec<Expression> = Vec::new();
44272        loop {
44273            match self.parse_expression() {
44274                Ok(expr) => args.push(expr),
44275                Err(_) => break,
44276            }
44277            if !self.match_token(TokenType::Comma) {
44278                break;
44279            }
44280        }
44281
44282        if args.len() < 3 {
44283            // Simple decode with charset
44284            return Ok(Some(Expression::DecodeCase(Box::new(DecodeCase {
44285                expressions: args,
44286            }))));
44287        }
44288
44289        // Oracle DECODE: first arg is the expression being compared
44290        // Remaining args are search/result pairs, with optional default at end
44291        Ok(Some(Expression::DecodeCase(Box::new(DecodeCase {
44292            expressions: args,
44293        }))))
44294    }
44295
44296    /// parse_definer - MySQL DEFINER property
44297    /// Parses: DEFINER = user@host
44298    #[allow(unused_variables, unused_mut)]
44299    pub fn parse_definer(&mut self) -> Result<Option<Expression>> {
44300        // Optionally consume = sign
44301        self.match_token(TokenType::Eq);
44302
44303        // Parse the user part
44304        let user = self.parse_id_var()?;
44305        if user.is_none() {
44306            return Ok(None);
44307        }
44308
44309        // Expect @ symbol
44310        if !self.match_token(TokenType::DAt) {
44311            return Ok(None);
44312        }
44313
44314        // Parse the host part (can be identifier or % wildcard)
44315        let host = if let Some(id) = self.parse_id_var()? {
44316            id
44317        } else if self.match_token(TokenType::Mod) {
44318            // % wildcard for any host
44319            Expression::Identifier(Identifier::new(self.previous().text.clone()))
44320        } else {
44321            return Ok(None);
44322        };
44323
44324        // Combine user@host into a string
44325        let user_str = match &user {
44326            Some(Expression::Identifier(id)) => id.name.clone(),
44327            _ => "".to_string(),
44328        };
44329        let host_str = match &host {
44330            Expression::Identifier(id) => id.name.clone(),
44331            _ => "".to_string(),
44332        };
44333
44334        let definer_str = format!("{}@{}", user_str, host_str);
44335
44336        Ok(Some(Expression::DefinerProperty(Box::new(
44337            DefinerProperty {
44338                this: Box::new(Expression::Literal(Literal::String(definer_str))),
44339            },
44340        ))))
44341    }
44342
44343    /// parse_derived_table_values - Implemented from Python _parse_derived_table_values
44344    #[allow(unused_variables, unused_mut)]
44345    pub fn parse_derived_table_values(&mut self) -> Result<Option<Expression>> {
44346        if self.match_text_seq(&["VALUES"]) {
44347            return Ok(Some(Expression::Values(Box::new(Values {
44348                expressions: Vec::new(),
44349                alias: None,
44350                column_aliases: Vec::new(),
44351            }))));
44352        }
44353        if self.match_text_seq(&["FORMAT", "VALUES"]) {
44354            // Matched: FORMAT VALUES
44355            return Ok(None);
44356        }
44357        Ok(None)
44358    }
44359
44360    /// parse_dict_property - ClickHouse dictionary property
44361    /// Parses: property_name(kind(key1 value1, key2 value2, ...))
44362    /// property_name should be the already matched property keyword (LAYOUT, SOURCE, etc.)
44363    #[allow(unused_variables, unused_mut)]
44364    pub fn parse_dict_property(&mut self, property_name: &str) -> Result<Option<Expression>> {
44365        // Expect opening paren
44366        if !self.match_token(TokenType::LParen) {
44367            return Ok(None);
44368        }
44369
44370        // Parse the kind (e.g., HASHED, FLAT, CLICKHOUSE, CACHE, etc.)
44371        // Accept Var, Identifier, or keyword tokens as the kind name
44372        let kind_str = if self.is_identifier_token() || self.check_keyword() {
44373            self.advance().text.clone()
44374        } else {
44375            String::new()
44376        };
44377        if kind_str.is_empty() {
44378            return Err(self.parse_error("Expected dictionary property kind"));
44379        }
44380
44381        // Parse optional settings in nested parens
44382        let settings = if self.match_token(TokenType::LParen) {
44383            let mut setting_pairs = Vec::new();
44384            loop {
44385                let key = if let Some(k) = self.parse_id_var()? {
44386                    Some(k)
44387                } else if self.is_safe_keyword_as_identifier() || self.check_keyword() {
44388                    let name = self.advance().text.clone();
44389                    Some(Expression::Identifier(Identifier::new(name)))
44390                } else if !self.check(TokenType::RParen) && !self.check(TokenType::Comma) {
44391                    let name = self.advance().text.clone();
44392                    Some(Expression::Identifier(Identifier::new(name)))
44393                } else {
44394                    None
44395                };
44396                // ClickHouse: STRUCTURE (...) contains column defs without commas — consume balanced parens
44397                let is_structure = key.as_ref().map_or(false, |k| {
44398                    matches!(k, Expression::Identifier(id) if id.name.eq_ignore_ascii_case("STRUCTURE"))
44399                });
44400                let value = if is_structure && self.check(TokenType::LParen) {
44401                    let mut raw = String::new();
44402                    let mut depth = 0i32;
44403                    while !self.is_at_end() {
44404                        let tok = self.advance();
44405                        match tok.token_type {
44406                            TokenType::LParen => {
44407                                depth += 1;
44408                                raw.push('(');
44409                            }
44410                            TokenType::RParen => {
44411                                depth -= 1;
44412                                if depth == 0 {
44413                                    raw.push(')');
44414                                    break;
44415                                }
44416                                raw.push(')');
44417                            }
44418                            _ => {
44419                                if !raw.is_empty() && !raw.ends_with('(') {
44420                                    raw.push(' ');
44421                                }
44422                                raw.push_str(&tok.text);
44423                            }
44424                        }
44425                    }
44426                    Some(Expression::Var(Box::new(Var { this: raw })))
44427                } else {
44428                    self.parse_primary_or_var()?
44429                };
44430                if key.is_none() && value.is_none() {
44431                    break;
44432                }
44433                if let (Some(k), Some(v)) = (key, value) {
44434                    // Store as a tuple-like expression
44435                    setting_pairs.push(Expression::Tuple(Box::new(Tuple {
44436                        expressions: vec![k, v],
44437                    })));
44438                }
44439                // ClickHouse dict properties are space-separated, not comma-separated
44440                // e.g. SOURCE(CLICKHOUSE(HOST 'localhost' PORT tcpPort() DB 'test'))
44441                // Accept optional comma but don't require it
44442                self.match_token(TokenType::Comma);
44443                // Break if we see RParen (end of settings)
44444                if self.check(TokenType::RParen) {
44445                    break;
44446                }
44447            }
44448            self.expect(TokenType::RParen)?;
44449            if !setting_pairs.is_empty() {
44450                Some(Box::new(Expression::Tuple(Box::new(Tuple {
44451                    expressions: setting_pairs,
44452                }))))
44453            } else {
44454                None
44455            }
44456        } else {
44457            None
44458        };
44459
44460        self.expect(TokenType::RParen)?;
44461
44462        Ok(Some(Expression::DictProperty(Box::new(DictProperty {
44463            this: Box::new(Expression::Identifier(Identifier::new(
44464                property_name.to_string(),
44465            ))),
44466            kind: kind_str,
44467            settings,
44468        }))))
44469    }
44470
44471    /// parse_dict_range - Implemented from Python _parse_dict_range
44472    /// Parses dictionary range specification: (MIN min_val MAX max_val) or (max_val)
44473    pub fn parse_dict_range(&mut self, property_name: &str) -> Result<Option<Expression>> {
44474        // Expect opening paren
44475        self.expect(TokenType::LParen)?;
44476
44477        // Prefer id/var first for dictionary bounds to avoid function-keyword ambiguity
44478        // such as `MIN discount_start_date MAX discount_end_date`.
44479        let parse_bound = |parser: &mut Parser| -> Result<Option<Expression>> {
44480            // Handle negative numbers: -1, -100, etc.
44481            if parser.check(TokenType::Dash)
44482                && parser
44483                    .peek_nth(1)
44484                    .is_some_and(|t| t.token_type == TokenType::Number)
44485            {
44486                parser.advance(); // consume -
44487                let num = parser.advance().text.clone();
44488                return Ok(Some(Expression::Literal(Literal::Number(format!(
44489                    "-{}",
44490                    num
44491                )))));
44492            }
44493            if let Some(id) = parser.parse_id_var()? {
44494                return Ok(Some(id));
44495            }
44496            parser.parse_primary_or_var()
44497        };
44498
44499        let (min_val, max_val) = if self.peek().text.eq_ignore_ascii_case("MIN") {
44500            self.skip(); // consume MIN
44501            let min = parse_bound(self)?;
44502            if self.peek().text.eq_ignore_ascii_case("MAX") {
44503                self.skip(); // consume MAX
44504            }
44505            let max = parse_bound(self)?;
44506            (min, max)
44507        } else {
44508            let max = parse_bound(self)?;
44509            let min = Some(Expression::Literal(Literal::Number("0".to_string())));
44510            (min, max)
44511        };
44512
44513        // Match closing paren
44514        self.expect(TokenType::RParen)?;
44515
44516        Ok(Some(Expression::DictRange(Box::new(DictRange {
44517            this: Box::new(Expression::Var(Box::new(Var {
44518                this: property_name.to_string(),
44519            }))),
44520            min: min_val.map(Box::new),
44521            max: max_val.map(Box::new),
44522        }))))
44523    }
44524
44525    /// parse_disjunction - Parses OR expressions
44526    /// Python: _parse_disjunction
44527    /// Delegates to the existing parse_or in the operator precedence chain
44528    pub fn parse_disjunction(&mut self) -> Result<Option<Expression>> {
44529        match self.parse_or() {
44530            Ok(expr) => Ok(Some(expr)),
44531            Err(_) => Ok(None),
44532        }
44533    }
44534
44535    /// parse_distkey - Redshift DISTKEY property for distribution key
44536    /// Parses: DISTKEY(column_name)
44537    #[allow(unused_variables, unused_mut)]
44538    pub fn parse_distkey(&mut self) -> Result<Option<Expression>> {
44539        // Parse wrapped column identifier (in parentheses)
44540        if !self.match_token(TokenType::LParen) {
44541            return Ok(None);
44542        }
44543
44544        let column = self.parse_id_var()?;
44545        if column.is_none() {
44546            return Ok(None);
44547        }
44548
44549        self.match_token(TokenType::RParen);
44550
44551        Ok(Some(Expression::DistKeyProperty(Box::new(
44552            DistKeyProperty {
44553                this: Box::new(column.unwrap()),
44554            },
44555        ))))
44556    }
44557
44558    /// parse_distributed_property - Implemented from Python _parse_distributed_property
44559    #[allow(unused_variables, unused_mut)]
44560    /// parse_distributed_property - Parses DISTRIBUTED BY property
44561    /// Python: parser.py:2462-2481
44562    pub fn parse_distributed_property(&mut self) -> Result<Option<Expression>> {
44563        let mut kind = "HASH".to_string();
44564        let mut expressions = Vec::new();
44565
44566        if self.match_text_seq(&["BY", "HASH"]) {
44567            // Parse column list: (col1, col2, ...)
44568            if let Some(wrapped) = self.parse_wrapped_id_vars()? {
44569                if let Expression::Tuple(t) = wrapped {
44570                    expressions = t.expressions;
44571                }
44572            }
44573        } else if self.match_text_seq(&["BY", "RANDOM"]) {
44574            kind = "RANDOM".to_string();
44575        } else {
44576            return Ok(None);
44577        }
44578
44579        // Parse optional BUCKETS
44580        let buckets = if self.match_text_seq(&["BUCKETS"]) {
44581            if !self.match_text_seq(&["AUTO"]) {
44582                self.parse_number()?
44583            } else {
44584                None
44585            }
44586        } else {
44587            None
44588        };
44589
44590        // Parse optional ORDER BY
44591        let order = self.parse_order()?;
44592
44593        Ok(Some(Expression::DistributedByProperty(Box::new(
44594            DistributedByProperty {
44595                expressions,
44596                kind,
44597                buckets: buckets.map(Box::new),
44598                order: order.map(Box::new),
44599            },
44600        ))))
44601    }
44602
44603    /// Parse DROP COLUMN in ALTER TABLE
44604    /// Note: Main ALTER TABLE DROP COLUMN logic is in parse_alter_table -> AlterTableAction::DropColumn
44605    pub fn parse_drop_column(&mut self) -> Result<Option<Expression>> {
44606        // Optionally match COLUMN keyword
44607        self.match_token(TokenType::Column);
44608
44609        // Parse IF EXISTS
44610        let _if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
44611
44612        // Parse the column identifier
44613        if let Some(column) = self.parse_identifier()? {
44614            // Check for CASCADE
44615            let _cascade = self.match_text_seq(&["CASCADE"]);
44616            // Return the column as an identifier (the caller handles the drop semantics)
44617            Ok(Some(column))
44618        } else {
44619            Ok(None)
44620        }
44621    }
44622
44623    /// Parse DROP PARTITION in ALTER TABLE
44624    /// Note: Main ALTER TABLE DROP PARTITION logic is in parse_alter_table -> AlterTableAction::DropPartition
44625    pub fn parse_drop_partition(&mut self) -> Result<Option<Expression>> {
44626        self.parse_drop_partition_with_exists(false)
44627    }
44628
44629    /// Parse DROP PARTITION with exists flag
44630    pub fn parse_drop_partition_with_exists(&mut self, exists: bool) -> Result<Option<Expression>> {
44631        // Parse one or more partitions
44632        let mut partitions = Vec::new();
44633
44634        loop {
44635            // Parse PARTITION (key = value, ...)
44636            if self.match_token(TokenType::Partition) {
44637                if self.match_token(TokenType::LParen) {
44638                    // Parse partition expressions
44639                    let mut exprs = Vec::new();
44640                    loop {
44641                        let expr = self.parse_expression()?;
44642                        exprs.push(expr);
44643                        if !self.match_token(TokenType::Comma) {
44644                            break;
44645                        }
44646                    }
44647                    self.match_token(TokenType::RParen);
44648                    partitions.push(Expression::Tuple(Box::new(Tuple { expressions: exprs })));
44649                }
44650            } else {
44651                break;
44652            }
44653
44654            if !self.match_token(TokenType::Comma) {
44655                break;
44656            }
44657        }
44658
44659        if partitions.is_empty() {
44660            Ok(None)
44661        } else {
44662            Ok(Some(Expression::DropPartition(Box::new(DropPartition {
44663                expressions: partitions,
44664                exists,
44665            }))))
44666        }
44667    }
44668
44669    /// parse_equality - Parses comparison/equality expressions (= <> < > <= >=)
44670    /// Python: _parse_equality
44671    /// Delegates to the existing parse_comparison in the operator precedence chain
44672    pub fn parse_equality(&mut self) -> Result<Option<Expression>> {
44673        match self.parse_comparison() {
44674            Ok(expr) => Ok(Some(expr)),
44675            Err(_) => Ok(None),
44676        }
44677    }
44678
44679    /// parse_escape - Parses ESCAPE clause for LIKE patterns
44680    /// Python: _parse_escape
44681    /// Returns the escape character/expression if ESCAPE keyword is found
44682    pub fn parse_escape(&mut self) -> Result<Option<Expression>> {
44683        if !self.match_token(TokenType::Escape) {
44684            return Ok(None);
44685        }
44686
44687        // Parse escape character (usually a string like '\')
44688        if let Some(escape_char) = self.parse_string()? {
44689            return Ok(Some(escape_char));
44690        }
44691
44692        // Or parse NULL
44693        if let Some(null_expr) = self.parse_null()? {
44694            return Ok(Some(null_expr));
44695        }
44696
44697        Ok(None)
44698    }
44699
44700    /// parse_exists - Implemented from Python _parse_exists
44701    #[allow(unused_variables, unused_mut)]
44702    pub fn parse_exists(&mut self) -> Result<Option<Expression>> {
44703        if self.match_text_seq(&["IF"]) {
44704            // Matched: IF
44705            return Ok(None);
44706        }
44707        Ok(None)
44708    }
44709
44710    /// parse_exponent - Parses exponent/power expressions
44711    /// Python: _parse_exponent
44712    /// In most dialects, EXPONENT is empty, so this delegates to parse_unary
44713    pub fn parse_exponent(&mut self) -> Result<Option<Expression>> {
44714        match self.parse_unary() {
44715            Ok(expr) => Ok(Some(expr)),
44716            Err(_) => Ok(None),
44717        }
44718    }
44719
44720    /// parse_expressions - Parse comma-separated expressions
44721    /// Returns a Tuple containing all expressions, or None if empty
44722    #[allow(unused_variables, unused_mut)]
44723    pub fn parse_expressions(&mut self) -> Result<Option<Expression>> {
44724        let expressions = self.parse_expression_list()?;
44725        if expressions.is_empty() {
44726            return Ok(None);
44727        }
44728        if expressions.len() == 1 {
44729            return Ok(expressions.into_iter().next());
44730        }
44731        Ok(Some(Expression::Tuple(Box::new(Tuple { expressions }))))
44732    }
44733
44734    /// parse_extract - Ported from Python _parse_extract
44735    /// Parses EXTRACT(field FROM expression) function
44736    #[allow(unused_variables, unused_mut)]
44737    pub fn parse_extract(&mut self) -> Result<Option<Expression>> {
44738        // Parse the field (YEAR, MONTH, DAY, HOUR, etc.)
44739        let field_name = if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
44740            let token = self.advance();
44741            token.text.to_ascii_uppercase()
44742        } else {
44743            return Ok(None);
44744        };
44745
44746        // Convert field name to DateTimeField
44747        let field = match field_name.as_str() {
44748            "YEAR" => DateTimeField::Year,
44749            "MONTH" => DateTimeField::Month,
44750            "DAY" => DateTimeField::Day,
44751            "HOUR" => DateTimeField::Hour,
44752            "MINUTE" => DateTimeField::Minute,
44753            "SECOND" => DateTimeField::Second,
44754            "MILLISECOND" | "MILLISECONDS" | "MS" => DateTimeField::Millisecond,
44755            "MICROSECOND" | "MICROSECONDS" | "US" => DateTimeField::Microsecond,
44756            "DOW" | "DAYOFWEEK" => DateTimeField::DayOfWeek,
44757            "DOY" | "DAYOFYEAR" => DateTimeField::DayOfYear,
44758            "WEEK" => DateTimeField::Week,
44759            "QUARTER" => DateTimeField::Quarter,
44760            "EPOCH" => DateTimeField::Epoch,
44761            "TIMEZONE" => DateTimeField::Timezone,
44762            "TIMEZONE_HOUR" => DateTimeField::TimezoneHour,
44763            "TIMEZONE_MINUTE" => DateTimeField::TimezoneMinute,
44764            "DATE" => DateTimeField::Date,
44765            "TIME" => DateTimeField::Time,
44766            other => DateTimeField::Custom(other.to_string()),
44767        };
44768
44769        // Expect FROM or comma
44770        if !self.match_token(TokenType::From) && !self.match_token(TokenType::Comma) {
44771            return Err(self.parse_error("Expected FROM or comma after EXTRACT field"));
44772        }
44773
44774        // Parse the expression to extract from
44775        let expression = self.parse_bitwise()?;
44776        let this = match expression {
44777            Some(expr) => self.try_clickhouse_func_arg_alias(expr),
44778            None => return Err(self.parse_error("Expected expression after FROM in EXTRACT")),
44779        };
44780
44781        Ok(Some(Expression::Extract(Box::new(ExtractFunc {
44782            this,
44783            field,
44784        }))))
44785    }
44786
44787    /// parse_factor - Parses multiplication/division expressions (* / % operators)
44788    /// Python: _parse_factor
44789    /// Delegates to the existing parse_multiplication in the operator precedence chain
44790    pub fn parse_factor(&mut self) -> Result<Option<Expression>> {
44791        // Delegate to the existing multiplication parsing
44792        match self.parse_multiplication() {
44793            Ok(expr) => Ok(Some(expr)),
44794            Err(_) => Ok(None),
44795        }
44796    }
44797
44798    /// parse_fallback - Implemented from Python _parse_fallback
44799    #[allow(unused_variables, unused_mut)]
44800    pub fn parse_fallback(&mut self) -> Result<Option<Expression>> {
44801        if self.match_text_seq(&["PROTECTION"]) {
44802            return Ok(Some(Expression::FallbackProperty(Box::new(
44803                FallbackProperty {
44804                    no: None,
44805                    protection: None,
44806                },
44807            ))));
44808        }
44809        Ok(None)
44810    }
44811
44812    /// parse_field - Parse a field (column name, literal, or expression)
44813    /// Python: field = self._parse_primary() or self._parse_function() or self._parse_id_var()
44814    pub fn parse_field(&mut self) -> Result<Option<Expression>> {
44815        // Try parsing literals first
44816        if let Some(expr) = self.parse_string()? {
44817            return Ok(Some(expr));
44818        }
44819        if let Some(expr) = self.parse_number()? {
44820            return Ok(Some(expr));
44821        }
44822        if let Some(expr) = self.parse_boolean()? {
44823            return Ok(Some(expr));
44824        }
44825        if let Some(expr) = self.parse_null()? {
44826            return Ok(Some(expr));
44827        }
44828        if let Some(expr) = self.parse_star()? {
44829            return Ok(Some(expr));
44830        }
44831        // Try parsing identifier
44832        if let Some(expr) = self.parse_identifier()? {
44833            return Ok(Some(expr));
44834        }
44835        // Try parsing a variable/identifier
44836        if let Some(expr) = self.parse_var()? {
44837            return Ok(Some(expr));
44838        }
44839        // Allow keywords as identifiers in field context (e.g., "schema" as a field name)
44840        if self.check_keyword() {
44841            let token = self.advance();
44842            return Ok(Some(Expression::Identifier(Identifier {
44843                name: token.text,
44844                quoted: false,
44845                trailing_comments: Vec::new(),
44846                span: None,
44847            })));
44848        }
44849        Ok(None)
44850    }
44851
44852    /// parse_field_def - Ported from Python _parse_field_def
44853    /// Parses a field definition (column name + type + optional constraints)
44854    #[allow(unused_variables, unused_mut)]
44855    pub fn parse_field_def(&mut self) -> Result<Option<Expression>> {
44856        // First parse the field name (identifier)
44857        let field = self.parse_field()?;
44858
44859        if field.is_none() {
44860            return Ok(None);
44861        }
44862
44863        // Parse the column definition with the field as the name
44864        self.parse_column_def_with_field(field)
44865    }
44866
44867    /// Helper to parse a column definition with a pre-parsed field name
44868    fn parse_column_def_with_field(
44869        &mut self,
44870        field: Option<Expression>,
44871    ) -> Result<Option<Expression>> {
44872        if field.is_none() {
44873            return Ok(None);
44874        }
44875
44876        let this = field.unwrap();
44877
44878        // Get the identifier from the expression and preserve quoted-identifier state.
44879        let name_ident = match &this {
44880            Expression::Column(col) => col.name.clone(),
44881            Expression::Identifier(id) => id.clone(),
44882            Expression::Var(v) => Identifier::new(v.this.clone()),
44883            _ => return Ok(None),
44884        };
44885
44886        // Parse the data type using parse_data_type_optional (which handles unknown types gracefully)
44887        let data_type = match self.parse_data_type_optional()? {
44888            Some(dt) => dt,
44889            None => DataType::Unknown,
44890        };
44891
44892        // Create ColumnDef with default values
44893        let mut col_def = ColumnDef::new(name_ident.name.clone(), data_type);
44894        col_def.name = name_ident;
44895
44896        // Check for FOR ORDINALITY (JSON table columns)
44897        if self.match_text_seq(&["FOR", "ORDINALITY"]) {
44898            return Ok(Some(Expression::ColumnDef(Box::new(col_def))));
44899        }
44900
44901        // Parse constraints and extract specific constraint values
44902        loop {
44903            if let Some(constraint) = self.parse_column_constraint()? {
44904                // Check specific constraint types
44905                match &constraint {
44906                    Expression::NotNullColumnConstraint(_) => {
44907                        col_def.nullable = Some(false);
44908                        col_def.constraints.push(ColumnConstraint::NotNull);
44909                    }
44910                    Expression::PrimaryKeyColumnConstraint(_) => {
44911                        col_def.primary_key = true;
44912                        col_def.constraints.push(ColumnConstraint::PrimaryKey);
44913                    }
44914                    Expression::UniqueColumnConstraint(_) => {
44915                        col_def.unique = true;
44916                        col_def.constraints.push(ColumnConstraint::Unique);
44917                    }
44918                    Expression::DefaultColumnConstraint(dc) => {
44919                        col_def.default = Some((*dc.this).clone());
44920                        col_def
44921                            .constraints
44922                            .push(ColumnConstraint::Default((*dc.this).clone()));
44923                    }
44924                    Expression::AutoIncrementColumnConstraint(_) => {
44925                        col_def.auto_increment = true;
44926                    }
44927                    Expression::CommentColumnConstraint(_) => {
44928                        // Comment is a unit struct, we'd need the actual comment text
44929                    }
44930                    Expression::CheckColumnConstraint(cc) => {
44931                        col_def
44932                            .constraints
44933                            .push(ColumnConstraint::Check((*cc.this).clone()));
44934                    }
44935                    Expression::PathColumnConstraint(pc) => {
44936                        col_def
44937                            .constraints
44938                            .push(ColumnConstraint::Path((*pc.this).clone()));
44939                        col_def.constraint_order.push(ConstraintType::Path);
44940                    }
44941                    _ => {}
44942                }
44943            } else if matches!(
44944                self.config.dialect,
44945                Some(crate::dialects::DialectType::ClickHouse)
44946            ) && self.match_identifier("ALIAS")
44947            {
44948                // ClickHouse: ALIAS expr
44949                let expr = self.parse_or()?;
44950                col_def.alias_expr = Some(Box::new(expr));
44951            } else if matches!(
44952                self.config.dialect,
44953                Some(crate::dialects::DialectType::ClickHouse)
44954            ) && self.check(TokenType::Materialized)
44955                && !self.check_next(TokenType::View)
44956            {
44957                // ClickHouse: MATERIALIZED expr
44958                self.skip(); // consume MATERIALIZED
44959                let expr = self.parse_or()?;
44960                col_def.materialized_expr = Some(Box::new(expr));
44961            } else if matches!(
44962                self.config.dialect,
44963                Some(crate::dialects::DialectType::ClickHouse)
44964            ) && self.match_identifier("EPHEMERAL")
44965            {
44966                // ClickHouse: EPHEMERAL [expr]
44967                if !self.check(TokenType::Comma)
44968                    && !self.check(TokenType::RParen)
44969                    && !self.is_at_end()
44970                    && !self.check_identifier("CODEC")
44971                    && !self.check_identifier("TTL")
44972                    && !self.check(TokenType::Comment)
44973                {
44974                    let expr = self.parse_bitwise()?.unwrap_or(Expression::Null(Null));
44975                    col_def.ephemeral = Some(Some(Box::new(expr)));
44976                } else {
44977                    col_def.ephemeral = Some(None);
44978                }
44979            } else if matches!(
44980                self.config.dialect,
44981                Some(crate::dialects::DialectType::ClickHouse)
44982            ) && self.check_identifier("CODEC")
44983            {
44984                // ClickHouse: CODEC(LZ4HC(9), ZSTD, DELTA)
44985                self.skip(); // consume CODEC
44986                self.expect(TokenType::LParen)?;
44987                let start = self.current;
44988                let mut depth = 1;
44989                while !self.is_at_end() && depth > 0 {
44990                    if self.check(TokenType::LParen) {
44991                        depth += 1;
44992                    }
44993                    if self.check(TokenType::RParen) {
44994                        depth -= 1;
44995                        if depth == 0 {
44996                            break;
44997                        }
44998                    }
44999                    self.skip();
45000                }
45001                let codec_text = self.tokens_to_sql(start, self.current);
45002                self.expect(TokenType::RParen)?;
45003                col_def.codec = Some(codec_text);
45004            } else if matches!(
45005                self.config.dialect,
45006                Some(crate::dialects::DialectType::ClickHouse)
45007            ) && self.match_identifier("TTL")
45008            {
45009                // ClickHouse: TTL expr
45010                let expr = self.parse_expression()?;
45011                col_def.ttl_expr = Some(Box::new(expr));
45012            } else {
45013                break;
45014            }
45015        }
45016
45017        Ok(Some(Expression::ColumnDef(Box::new(col_def))))
45018    }
45019
45020    /// parse_foreign_key - Implemented from Python _parse_foreign_key
45021    /// Calls: parse_key_constraint_options, parse_wrapped_id_vars, parse_references
45022    #[allow(unused_variables, unused_mut)]
45023    pub fn parse_foreign_key(&mut self) -> Result<Option<Expression>> {
45024        if self.match_text_seq(&["NO", "ACTION"]) {
45025            return Ok(Some(Expression::ForeignKey(Box::new(ForeignKey {
45026                expressions: Vec::new(),
45027                reference: None,
45028                delete: None,
45029                update: None,
45030                options: Vec::new(),
45031            }))));
45032        }
45033        Ok(None)
45034    }
45035
45036    /// parse_format_json - Implemented from Python _parse_format_json
45037    #[allow(unused_variables, unused_mut)]
45038    pub fn parse_format_json(&mut self) -> Result<Option<Expression>> {
45039        if self.match_text_seq(&["FORMAT", "JSON"]) {
45040            // Matched: FORMAT JSON
45041            return Ok(None);
45042        }
45043        Ok(None)
45044    }
45045
45046    /// parse_format_name - Snowflake FILE_FORMAT = format_name property
45047    /// Parses: format_name (string or identifier)
45048    #[allow(unused_variables, unused_mut)]
45049    pub fn parse_format_name(&mut self) -> Result<Option<Expression>> {
45050        // Try to parse a string first, then fall back to table parts
45051        let value = if let Some(s) = self.parse_string()? {
45052            s
45053        } else if let Some(tp) = self.parse_table_parts()? {
45054            tp
45055        } else {
45056            return Ok(None);
45057        };
45058
45059        Ok(Some(Expression::Property(Box::new(Property {
45060            this: Box::new(Expression::Identifier(Identifier::new(
45061                "FORMAT_NAME".to_string(),
45062            ))),
45063            value: Some(Box::new(value)),
45064        }))))
45065    }
45066
45067    /// parse_freespace - Teradata FREESPACE property
45068    /// Parses: FREESPACE = number [PERCENT]
45069    #[allow(unused_variables, unused_mut)]
45070    pub fn parse_freespace(&mut self) -> Result<Option<Expression>> {
45071        // Optionally consume = sign
45072        self.match_token(TokenType::Eq);
45073
45074        // Parse the number value
45075        let this = self.parse_number()?;
45076        if this.is_none() {
45077            return Ok(None);
45078        }
45079
45080        // Check for PERCENT keyword
45081        let percent = if self.match_token(TokenType::Percent) {
45082            Some(Box::new(Expression::Boolean(BooleanLiteral {
45083                value: true,
45084            })))
45085        } else {
45086            None
45087        };
45088
45089        Ok(Some(Expression::FreespaceProperty(Box::new(
45090            FreespaceProperty {
45091                this: Box::new(this.unwrap()),
45092                percent,
45093            },
45094        ))))
45095    }
45096
45097    /// parse_function - Ported from Python _parse_function
45098    /// Parses function calls like func_name(args) or {fn func_name(args)} (ODBC syntax)
45099    pub fn parse_function(&mut self) -> Result<Option<Expression>> {
45100        // Check for ODBC escape syntax: {fn function_call}
45101        let fn_syntax = if self.check(TokenType::LBrace) {
45102            if let Some(next) = self.tokens.get(self.current + 1) {
45103                if next.text.eq_ignore_ascii_case("FN") {
45104                    self.skip(); // consume {
45105                    self.skip(); // consume FN
45106                    true
45107                } else {
45108                    false
45109                }
45110            } else {
45111                false
45112            }
45113        } else {
45114            false
45115        };
45116
45117        let func = self.parse_function_call()?;
45118
45119        if fn_syntax {
45120            self.match_token(TokenType::RBrace);
45121        }
45122
45123        Ok(func)
45124    }
45125
45126    /// parse_function_args - Ported from Python _parse_function_args
45127    /// Parses the arguments inside a function call, handling aliases and key-value pairs
45128    pub fn parse_function_args_list(&mut self) -> Result<Vec<Expression>> {
45129        let mut args = Vec::new();
45130
45131        if self.check(TokenType::RParen) {
45132            return Ok(args);
45133        }
45134
45135        loop {
45136            // Try to parse expression with optional alias
45137            if let Some(expr) = self.parse_assignment()? {
45138                // Handle explicit AS alias inside function args (e.g. `tuple(1 AS "a", 2 AS "b")`)
45139                if self.match_token(TokenType::As) {
45140                    let alias_token = self.advance();
45141                    let alias_name = if alias_token.token_type == TokenType::QuotedIdentifier {
45142                        // Preserve quoted identifiers
45143                        let raw = alias_token.text.clone();
45144                        let mut ident = Identifier::new(raw);
45145                        ident.quoted = true;
45146                        ident
45147                    } else {
45148                        Identifier::new(alias_token.text.clone())
45149                    };
45150                    args.push(Expression::Alias(Box::new(crate::expressions::Alias {
45151                        this: expr,
45152                        alias: alias_name,
45153                        column_aliases: Vec::new(),
45154                        pre_alias_comments: Vec::new(),
45155                        trailing_comments: Vec::new(),
45156                        inferred_type: None,
45157                    })));
45158                } else {
45159                    args.push(expr);
45160                }
45161            }
45162
45163            if !self.match_token(TokenType::Comma) {
45164                break;
45165            }
45166        }
45167
45168        Ok(args)
45169    }
45170
45171    /// parse_function_call - Ported from Python _parse_function_call
45172    /// Parses a function call expression like func_name(arg1, arg2, ...)
45173    pub fn parse_function_call(&mut self) -> Result<Option<Expression>> {
45174        if self.is_at_end() {
45175            return Ok(None);
45176        }
45177
45178        let token = self.peek().clone();
45179        let token_type = token.token_type.clone();
45180        let name = token.text.clone();
45181        let _upper_name = name.to_ascii_uppercase();
45182
45183        // Check for no-paren functions like CURRENT_DATE, CURRENT_TIMESTAMP
45184        if self.is_no_paren_function() {
45185            // Check if next token is NOT a paren (so it's used without parens)
45186            if !self.check_next(TokenType::LParen) {
45187                self.skip();
45188                return Ok(Some(Expression::Function(Box::new(Function {
45189                    name, // Preserve original case; generator handles normalization
45190                    args: Vec::new(),
45191                    distinct: false,
45192                    trailing_comments: Vec::new(),
45193                    use_bracket_syntax: false,
45194                    no_parens: true,
45195                    quoted: false,
45196                    span: None,
45197                    inferred_type: None,
45198                }))));
45199            }
45200        }
45201
45202        // Must be followed by left paren
45203        if !self.check_next(TokenType::LParen) {
45204            return Ok(None);
45205        }
45206
45207        // Token must be a valid function name token
45208        let is_valid_func_token = matches!(
45209            token_type,
45210            TokenType::Identifier
45211                | TokenType::Var
45212                | TokenType::If
45213                | TokenType::Left
45214                | TokenType::Right
45215                | TokenType::Insert
45216                | TokenType::Replace
45217                | TokenType::Row
45218                | TokenType::Index
45219        );
45220        if !is_valid_func_token {
45221            return Ok(None);
45222        }
45223
45224        self.skip(); // consume function name
45225        self.skip(); // consume (
45226
45227        // Check for DISTINCT keyword
45228        let distinct = self.match_token(TokenType::Distinct);
45229
45230        // Parse arguments
45231        let args = self.parse_function_args_list()?;
45232
45233        self.match_token(TokenType::RParen);
45234
45235        // Handle window specifications
45236        let func_expr = Expression::Function(Box::new(Function {
45237            name, // Preserve original case; generator handles normalization
45238            args,
45239            distinct,
45240            trailing_comments: Vec::new(),
45241            use_bracket_syntax: false,
45242            no_parens: false,
45243            quoted: false,
45244            span: None,
45245            inferred_type: None,
45246        }));
45247
45248        // Check for OVER clause (window function)
45249        if self.match_token(TokenType::Over) {
45250            // Parse window spec - create a simple WindowSpec
45251            if self.match_token(TokenType::LParen) {
45252                // Use parse_window_spec_inner to handle DISTRIBUTE BY/SORT BY (Hive)
45253                let spec = self.parse_window_spec_inner()?;
45254                self.expect(TokenType::RParen)?;
45255
45256                if let Some(spec_expr) = spec {
45257                    return Ok(Some(spec_expr));
45258                }
45259            }
45260        }
45261
45262        Ok(Some(func_expr))
45263    }
45264
45265    /// parse_function_parameter - Ported from Python _parse_function_parameter
45266    /// Parses a function parameter in CREATE FUNCTION (name type [DEFAULT expr])
45267    pub fn parse_function_parameter(&mut self) -> Result<Option<Expression>> {
45268        // Parse optional parameter mode (IN, OUT, INOUT)
45269        let _mode = if self.match_texts(&["IN"]) {
45270            if self.match_texts(&["OUT"]) {
45271                Some(ParameterMode::InOut)
45272            } else {
45273                Some(ParameterMode::In)
45274            }
45275        } else if self.match_texts(&["OUT"]) {
45276            Some(ParameterMode::Out)
45277        } else if self.match_texts(&["INOUT"]) {
45278            Some(ParameterMode::InOut)
45279        } else {
45280            None
45281        };
45282
45283        // Parse parameter name (optional in some dialects)
45284        let name_expr = self.parse_id_var()?;
45285        let name = name_expr.and_then(|n| match n {
45286            Expression::Identifier(id) => Some(id),
45287            _ => None,
45288        });
45289
45290        // Parse data type - returns Result<DataType>, not Result<Option<DataType>>
45291        // We need to handle the case where we can't parse a data type
45292        let data_type_result = self.parse_data_type();
45293        let _data_type = match data_type_result {
45294            Ok(dt) => dt,
45295            Err(_) => return Ok(None),
45296        };
45297
45298        // Parse optional DEFAULT value
45299        let _default = if self.match_token(TokenType::Default) || self.match_texts(&["="]) {
45300            self.parse_disjunction()?
45301        } else {
45302            None
45303        };
45304
45305        // Return the name as a Column expression
45306        Ok(Some(Expression::boxed_column(Column {
45307            name: Identifier {
45308                name: name.map(|n| n.name).unwrap_or_default(),
45309                quoted: false,
45310                trailing_comments: Vec::new(),
45311                span: None,
45312            },
45313            table: None,
45314            join_mark: false,
45315            trailing_comments: Vec::new(),
45316            span: None,
45317            inferred_type: None,
45318        })))
45319    }
45320
45321    /// parse_gap_fill - Ported from Python _parse_gap_fill
45322    #[allow(unused_variables, unused_mut)]
45323    /// parse_gap_fill - Parses GAP_FILL function for time series
45324    /// Example: GAP_FILL(TABLE t, ts_column, bucket_width, partitioning_columns, value_columns)
45325    pub fn parse_gap_fill(&mut self) -> Result<Option<Expression>> {
45326        // Optional TABLE keyword
45327        self.match_token(TokenType::Table);
45328
45329        // Parse the table reference
45330        let this = self.parse_table()?;
45331        if this.is_none() {
45332            return Ok(None);
45333        }
45334
45335        // Parse comma-separated arguments
45336        self.match_token(TokenType::Comma);
45337        let mut args = self.parse_expression_list()?;
45338
45339        // Extract arguments by position
45340        let ts_column = args.get(0).cloned().map(Box::new);
45341        let bucket_width = args.get(1).cloned().map(Box::new);
45342        let partitioning_columns = args.get(2).cloned().map(Box::new);
45343        let value_columns = args.get(3).cloned().map(Box::new);
45344
45345        Ok(Some(Expression::GapFill(Box::new(GapFill {
45346            this: Box::new(this.unwrap()),
45347            ts_column,
45348            bucket_width,
45349            partitioning_columns,
45350            value_columns,
45351            origin: None,
45352            ignore_nulls: None,
45353        }))))
45354    }
45355
45356    /// parse_semantic_view - Parse Snowflake SEMANTIC_VIEW function
45357    /// Example: SEMANTIC_VIEW(foo METRICS a.b, a.c DIMENSIONS a.b, a.c WHERE a.b > '1995-01-01')
45358    pub fn parse_semantic_view(&mut self) -> Result<Expression> {
45359        // Parse the table/view reference as a primary expression (identifier or qualified name)
45360        let this = self.parse_primary()?;
45361
45362        let mut metrics = None;
45363        let mut dimensions = None;
45364        let mut facts = None;
45365        let mut where_clause = None;
45366
45367        // Parse optional clauses: METRICS, DIMENSIONS, FACTS, WHERE
45368        while !self.check(TokenType::RParen) && !self.is_at_end() {
45369            if self.match_identifier("METRICS") {
45370                // Parse comma-separated expressions until next keyword or )
45371                let exprs = self.parse_semantic_view_list()?;
45372                metrics = Some(Box::new(Expression::Tuple(Box::new(Tuple {
45373                    expressions: exprs,
45374                }))));
45375            } else if self.match_identifier("DIMENSIONS") {
45376                let exprs = self.parse_semantic_view_list()?;
45377                dimensions = Some(Box::new(Expression::Tuple(Box::new(Tuple {
45378                    expressions: exprs,
45379                }))));
45380            } else if self.match_identifier("FACTS") {
45381                let exprs = self.parse_semantic_view_list()?;
45382                facts = Some(Box::new(Expression::Tuple(Box::new(Tuple {
45383                    expressions: exprs,
45384                }))));
45385            } else if self.match_token(TokenType::Where) {
45386                // Parse the WHERE expression
45387                where_clause = Some(Box::new(self.parse_expression()?));
45388                // WHERE is the last clause, break after parsing it
45389                break;
45390            } else {
45391                // Unknown token
45392                break;
45393            }
45394        }
45395
45396        Ok(Expression::SemanticView(Box::new(SemanticView {
45397            this: Box::new(this),
45398            metrics,
45399            dimensions,
45400            facts,
45401            where_: where_clause,
45402        })))
45403    }
45404
45405    /// Helper to parse comma-separated expression list for SEMANTIC_VIEW clauses
45406    /// Stops at METRICS, DIMENSIONS, FACTS, WHERE, or )
45407    /// Each element can have an optional AS alias: expr AS name
45408    fn parse_semantic_view_list(&mut self) -> Result<Vec<Expression>> {
45409        let first = self.parse_semantic_view_element()?;
45410        let mut exprs = vec![first];
45411        while self.match_token(TokenType::Comma) {
45412            // Check if next token is a keyword that starts a new clause
45413            if self.check_identifier("METRICS")
45414                || self.check_identifier("DIMENSIONS")
45415                || self.check_identifier("FACTS")
45416                || self.check(TokenType::Where)
45417                || self.check(TokenType::RParen)
45418            {
45419                break;
45420            }
45421            exprs.push(self.parse_semantic_view_element()?);
45422        }
45423        Ok(exprs)
45424    }
45425
45426    /// Parse a single SEMANTIC_VIEW element: expression [AS alias]
45427    fn parse_semantic_view_element(&mut self) -> Result<Expression> {
45428        let expr = self.parse_disjunction()?.ok_or_else(|| {
45429            self.parse_error("Expected expression in SEMANTIC_VIEW clause")
45430        })?;
45431        // Check for optional explicit AS alias
45432        if self.match_token(TokenType::As) {
45433            let alias = self.expect_identifier_or_keyword_with_quoted()?;
45434            Ok(Expression::Alias(Box::new(crate::expressions::Alias {
45435                this: expr,
45436                alias,
45437                column_aliases: Vec::new(),
45438                pre_alias_comments: Vec::new(),
45439                trailing_comments: Vec::new(),
45440                inferred_type: None,
45441            })))
45442        } else {
45443            Ok(expr)
45444        }
45445    }
45446
45447    /// parse_grant_principal - Implemented from Python _parse_grant_principal
45448    /// Calls: parse_id_var
45449    #[allow(unused_variables, unused_mut)]
45450    pub fn parse_grant_principal(&mut self) -> Result<Option<Expression>> {
45451        if self.match_texts(&["ROLE", "GROUP"]) {
45452            // Matched one of: ROLE, GROUP
45453            return Ok(None);
45454        }
45455        Ok(None)
45456    }
45457
45458    /// parse_grant_privilege - Parse a single privilege in GRANT/REVOKE
45459    /// Parses: SELECT, INSERT, UPDATE(col1, col2), DELETE, etc.
45460    #[allow(unused_variables, unused_mut)]
45461    pub fn parse_grant_privilege(&mut self) -> Result<Option<Expression>> {
45462        // Collect privilege keywords (SELECT, INSERT, UPDATE, DELETE, ALL PRIVILEGES, etc.)
45463        let mut privilege_parts = Vec::new();
45464
45465        // Keep consuming keywords until we hit a follow token
45466        // Follow tokens are: comma, ON, left paren
45467        while !self.is_at_end() {
45468            // Check if we've hit a follow token
45469            if self.check(TokenType::Comma)
45470                || self.check(TokenType::On)
45471                || self.check(TokenType::LParen)
45472            {
45473                break;
45474            }
45475
45476            // Get the current token text
45477            let text = self.peek().text.to_ascii_uppercase();
45478            privilege_parts.push(text);
45479            self.skip();
45480        }
45481
45482        if privilege_parts.is_empty() {
45483            return Ok(None);
45484        }
45485
45486        let privilege_str = privilege_parts.join(" ");
45487
45488        // Check for column list in parentheses (e.g., UPDATE(col1, col2))
45489        let expressions = if self.match_token(TokenType::LParen) {
45490            let mut columns = Vec::new();
45491            loop {
45492                if let Some(col) = self.parse_column()? {
45493                    columns.push(col);
45494                } else {
45495                    break;
45496                }
45497                if !self.match_token(TokenType::Comma) {
45498                    break;
45499                }
45500            }
45501            self.match_token(TokenType::RParen);
45502            columns
45503        } else {
45504            Vec::new()
45505        };
45506
45507        Ok(Some(Expression::GrantPrivilege(Box::new(GrantPrivilege {
45508            this: Box::new(Expression::Identifier(Identifier::new(privilege_str))),
45509            expressions,
45510        }))))
45511    }
45512
45513    /// parse_grant_revoke_common - Parses common parts of GRANT/REVOKE statements
45514    /// Python: _parse_grant_revoke_common
45515    /// Returns a Tuple containing (privileges, kind, securable)
45516    pub fn parse_grant_revoke_common(&mut self) -> Result<Option<Expression>> {
45517        // Parse privileges (CSV of grant privileges)
45518        let mut privileges = Vec::new();
45519        loop {
45520            if let Some(priv_expr) = self.parse_grant_privilege()? {
45521                privileges.push(priv_expr);
45522            }
45523            if !self.match_token(TokenType::Comma) {
45524                break;
45525            }
45526        }
45527
45528        // Match ON keyword
45529        self.match_token(TokenType::On);
45530
45531        // Parse kind (TABLE, VIEW, SCHEMA, DATABASE, etc.)
45532        let kind = if self.match_texts(&[
45533            "TABLE",
45534            "VIEW",
45535            "SCHEMA",
45536            "DATABASE",
45537            "SEQUENCE",
45538            "FUNCTION",
45539            "PROCEDURE",
45540            "INDEX",
45541            "TYPE",
45542            "TABLESPACE",
45543            "ROLE",
45544            "USER",
45545        ]) {
45546            let kind_text = self.previous().text.to_ascii_uppercase();
45547            Some(Expression::Var(Box::new(Var { this: kind_text })))
45548        } else {
45549            None
45550        };
45551
45552        // Try to parse securable (table parts)
45553        let securable = self.parse_table_parts()?;
45554
45555        // Return as Tuple with three elements: privileges_list, kind, securable
45556        let privileges_expr = Expression::Tuple(Box::new(Tuple {
45557            expressions: privileges,
45558        }));
45559
45560        let mut result_exprs = vec![privileges_expr];
45561
45562        if let Some(k) = kind {
45563            result_exprs.push(k);
45564        } else {
45565            result_exprs.push(Expression::Null(Null));
45566        }
45567
45568        if let Some(s) = securable {
45569            result_exprs.push(s);
45570        } else {
45571            result_exprs.push(Expression::Null(Null));
45572        }
45573
45574        Ok(Some(Expression::Tuple(Box::new(Tuple {
45575            expressions: result_exprs,
45576        }))))
45577    }
45578
45579    /// parse_group - Parse GROUP BY clause
45580    /// Python: if not self._match(TokenType.GROUP_BY): return None; expressions = self._parse_csv(self._parse_disjunction)
45581    pub fn parse_group(&mut self) -> Result<Option<Expression>> {
45582        // Check for GROUP BY token (which should be parsed as Group + By tokens)
45583        if !self.match_token(TokenType::Group) {
45584            return Ok(None);
45585        }
45586        // Consume BY if present
45587        self.match_token(TokenType::By);
45588
45589        // Check for optional ALL/DISTINCT
45590        // Some(true) = ALL, Some(false) = DISTINCT, None = no modifier
45591        let all = if self.match_token(TokenType::All) {
45592            Some(true)
45593        } else if self.match_token(TokenType::Distinct) {
45594            Some(false)
45595        } else {
45596            None
45597        };
45598
45599        // Parse comma-separated expressions
45600        let mut expressions = Vec::new();
45601        loop {
45602            match self.parse_expression() {
45603                Ok(expr) => expressions.push(expr),
45604                Err(_) => break,
45605            }
45606            if !self.match_token(TokenType::Comma) {
45607                break;
45608            }
45609        }
45610
45611        // Handle TOTALS (ClickHouse)
45612        let totals = if self.match_text_seq(&["WITH", "TOTALS"]) {
45613            Some(Box::new(Expression::Boolean(BooleanLiteral {
45614                value: true,
45615            })))
45616        } else if self.match_text_seq(&["TOTALS"]) {
45617            Some(Box::new(Expression::Boolean(BooleanLiteral {
45618                value: true,
45619            })))
45620        } else {
45621            None
45622        };
45623
45624        Ok(Some(Expression::Group(Box::new(Group {
45625            expressions,
45626            grouping_sets: None,
45627            cube: None,
45628            rollup: None,
45629            totals,
45630            all,
45631        }))))
45632    }
45633
45634    /// parse_group_concat - Ported from Python _parse_group_concat
45635    #[allow(unused_variables, unused_mut)]
45636    /// parse_group_concat - Parses MySQL GROUP_CONCAT function
45637    /// Example: GROUP_CONCAT(DISTINCT col ORDER BY col SEPARATOR ',')
45638    pub fn parse_group_concat(&mut self) -> Result<Option<Expression>> {
45639        // Check for DISTINCT
45640        let distinct = self.match_token(TokenType::Distinct);
45641
45642        // Parse expression(s)
45643        let expr = self.parse_expression()?;
45644
45645        // Parse optional ORDER BY
45646        let order_by = if self.match_keywords(&[TokenType::Order, TokenType::By]) {
45647            let mut orderings = Vec::new();
45648            loop {
45649                let order_expr = self.parse_expression()?;
45650                let desc = if self.match_token(TokenType::Desc) {
45651                    true
45652                } else {
45653                    self.match_token(TokenType::Asc);
45654                    false
45655                };
45656                let nulls_first = if self.match_keywords(&[TokenType::Nulls, TokenType::First]) {
45657                    Some(true)
45658                } else if self.match_keywords(&[TokenType::Nulls, TokenType::Last]) {
45659                    Some(false)
45660                } else {
45661                    None
45662                };
45663                orderings.push(Ordered {
45664                    this: order_expr,
45665                    desc,
45666                    nulls_first,
45667                    explicit_asc: !desc,
45668                    with_fill: None,
45669                });
45670                if !self.match_token(TokenType::Comma) {
45671                    break;
45672                }
45673            }
45674            Some(orderings)
45675        } else {
45676            None
45677        };
45678
45679        // Parse optional SEPARATOR
45680        let separator = if self.match_token(TokenType::Separator) {
45681            self.parse_string()?
45682        } else {
45683            None
45684        };
45685
45686        Ok(Some(Expression::GroupConcat(Box::new(GroupConcatFunc {
45687            this: expr,
45688            separator,
45689            order_by,
45690            distinct,
45691            filter: None,
45692            inferred_type: None,
45693        }))))
45694    }
45695
45696    /// parse_grouping_set - Delegates to parse_grouping_sets
45697    #[allow(unused_variables, unused_mut)]
45698    pub fn parse_grouping_set(&mut self) -> Result<Option<Expression>> {
45699        self.parse_grouping_sets()
45700    }
45701
45702    /// parse_grouping_sets - Ported from Python _parse_grouping_sets
45703    /// Parses GROUPING SETS ((...), (...)) in GROUP BY
45704    #[allow(unused_variables, unused_mut)]
45705    pub fn parse_grouping_sets(&mut self) -> Result<Option<Expression>> {
45706        // Check for GROUPING SETS keyword
45707        if !self.match_text_seq(&["GROUPING", "SETS"]) {
45708            return Ok(None);
45709        }
45710
45711        // Parse wrapped grouping sets
45712        self.expect(TokenType::LParen)?;
45713        let mut expressions = Vec::new();
45714
45715        if !self.check(TokenType::RParen) {
45716            loop {
45717                // Each grouping set can be:
45718                // - A nested GROUPING SETS
45719                // - CUBE or ROLLUP
45720                // - A parenthesized list
45721                // - A single expression
45722                if let Some(nested) = self.parse_grouping_sets()? {
45723                    expressions.push(nested);
45724                } else if let Some(cube_rollup) = self.parse_cube_or_rollup()? {
45725                    expressions.push(cube_rollup);
45726                } else if self.match_token(TokenType::LParen) {
45727                    // Parenthesized group
45728                    let mut group = Vec::new();
45729                    if !self.check(TokenType::RParen) {
45730                        loop {
45731                            match self.parse_bitwise() {
45732                                Ok(Some(expr)) => group.push(expr),
45733                                Ok(None) => break,
45734                                Err(e) => return Err(e),
45735                            }
45736                            if !self.match_token(TokenType::Comma) {
45737                                break;
45738                            }
45739                        }
45740                    }
45741                    self.expect(TokenType::RParen)?;
45742                    expressions.push(Expression::Tuple(Box::new(Tuple { expressions: group })));
45743                } else {
45744                    // Single expression
45745                    match self.parse_bitwise() {
45746                        Ok(Some(expr)) => expressions.push(expr),
45747                        Ok(None) => break,
45748                        Err(e) => return Err(e),
45749                    }
45750                }
45751
45752                if !self.match_token(TokenType::Comma) {
45753                    break;
45754                }
45755            }
45756        }
45757
45758        self.expect(TokenType::RParen)?;
45759
45760        Ok(Some(Expression::GroupingSets(Box::new(GroupingSets {
45761            expressions,
45762        }))))
45763    }
45764
45765    /// parse_having - Parse HAVING clause
45766    /// Python: if not self._match(TokenType.HAVING): return None; return exp.Having(this=self._parse_disjunction())
45767    pub fn parse_having(&mut self) -> Result<Option<Expression>> {
45768        if !self.match_token(TokenType::Having) {
45769            return Ok(None);
45770        }
45771        // Parse the condition expression
45772        let condition = self.parse_expression()?;
45773        Ok(Some(Expression::Having(Box::new(Having {
45774            this: condition,
45775            comments: Vec::new(),
45776        }))))
45777    }
45778
45779    /// parse_having_max - Implemented from Python _parse_having_max
45780    /// Calls: parse_column
45781    #[allow(unused_variables, unused_mut)]
45782    pub fn parse_having_max(&mut self) -> Result<Option<Expression>> {
45783        if self.match_texts(&["MAX", "MIN"]) {
45784            // Matched one of: MAX, MIN
45785            return Ok(None);
45786        }
45787        Ok(None)
45788    }
45789
45790    /// parse_heredoc - Implemented from Python _parse_heredoc
45791    /// Parses dollar-quoted strings: $$content$$, $tag$content$tag$
45792    pub fn parse_heredoc(&mut self) -> Result<Option<Expression>> {
45793        // Check if current token is a HEREDOC_STRING type
45794        if self.match_token(TokenType::HeredocString) {
45795            let text = self.previous().text.clone();
45796            return Ok(Some(Expression::Heredoc(Box::new(Heredoc {
45797                this: Box::new(Expression::Literal(Literal::String(text))),
45798                tag: None,
45799            }))));
45800        }
45801
45802        // Try to parse $...$ or $tag$...$tag$
45803        if !self.match_text_seq(&["$"]) {
45804            return Ok(None);
45805        }
45806
45807        // Collect the tag text (if any) and the closing marker
45808        let mut tags = vec!["$".to_string()];
45809        let mut tag_text: Option<String> = None;
45810
45811        // Check if next token is connected (no whitespace) and collect tag
45812        if !self.is_at_end() {
45813            let next_text = self.peek().text.to_ascii_uppercase();
45814            if next_text == "$" {
45815                // Simple $$ ... $$ case
45816                self.skip();
45817                tags.push("$".to_string());
45818            } else {
45819                // $tag$ ... $tag$ case
45820                self.skip();
45821                tag_text = Some(next_text.clone());
45822                tags.push(next_text);
45823
45824                // Expect closing $
45825                if self.match_text_seq(&["$"]) {
45826                    tags.push("$".to_string());
45827                } else {
45828                    return Err(self.parse_error("No closing $ found"));
45829                }
45830            }
45831        }
45832
45833        // Now collect content until we find the closing tags
45834        let mut content_parts = Vec::new();
45835        let closing_tag = tags.join("");
45836
45837        while !self.is_at_end() {
45838            // Build current sequence to check for closing tag
45839            let current_text = self.peek().text.clone();
45840
45841            // Check if we've reached the closing tag
45842            if current_text == "$" || current_text.eq_ignore_ascii_case(&closing_tag) {
45843                // Try to match the full closing sequence
45844                let start_pos = self.current;
45845                let mut matched = true;
45846                for expected in &tags {
45847                    if self.is_at_end()
45848                        || !self.peek().text.eq_ignore_ascii_case(expected)
45849                    {
45850                        matched = false;
45851                        break;
45852                    }
45853                    self.skip();
45854                }
45855                if matched {
45856                    // Found the closing tag
45857                    let content = content_parts.join(" ");
45858                    return Ok(Some(Expression::Heredoc(Box::new(Heredoc {
45859                        this: Box::new(Expression::Literal(Literal::String(content))),
45860                        tag: tag_text.map(|t| Box::new(Expression::Literal(Literal::String(t)))),
45861                    }))));
45862                }
45863                // Not the closing tag, backtrack and add to content
45864                self.current = start_pos;
45865            }
45866
45867            content_parts.push(self.advance().text.clone());
45868        }
45869
45870        Err(self.parse_error(&format!("No closing {} found", closing_tag)))
45871    }
45872
45873    /// parse_hint_body - Delegates to parse_hint_fallback_to_string
45874    #[allow(unused_variables, unused_mut)]
45875    pub fn parse_hint_body(&mut self) -> Result<Option<Expression>> {
45876        self.parse_hint_fallback_to_string()
45877    }
45878
45879    /// parse_hint_fallback_to_string - Parses remaining hint tokens as a raw string
45880    /// Python: _parse_hint_fallback_to_string
45881    /// Used when structured hint parsing fails - collects all remaining tokens
45882    pub fn parse_hint_fallback_to_string(&mut self) -> Result<Option<Expression>> {
45883        // Collect all remaining tokens as a string
45884        let mut parts = Vec::new();
45885        while !self.is_at_end() {
45886            let token = self.advance();
45887            parts.push(token.text.clone());
45888        }
45889
45890        if parts.is_empty() {
45891            return Ok(None);
45892        }
45893
45894        let hint_text = parts.join(" ");
45895        Ok(Some(Expression::Hint(Box::new(Hint {
45896            expressions: vec![HintExpression::Raw(hint_text)],
45897        }))))
45898    }
45899
45900    /// parse_hint_function_call - Delegates to parse_function_call
45901    #[allow(unused_variables, unused_mut)]
45902    pub fn parse_hint_function_call(&mut self) -> Result<Option<Expression>> {
45903        self.parse_function_call()
45904    }
45905
45906    /// parse_historical_data - Snowflake AT/BEFORE time travel clauses
45907    /// Parses: AT(TIMESTAMP => expr) or BEFORE(STATEMENT => 'id') etc.
45908    /// Reference: https://docs.snowflake.com/en/sql-reference/constructs/at-before
45909    #[allow(unused_variables, unused_mut)]
45910    pub fn parse_historical_data(&mut self) -> Result<Option<Expression>> {
45911        // Save position for backtracking
45912        let start_index = self.current;
45913
45914        // Check for AT, BEFORE, or END keywords
45915        let this = if self.match_texts(&["AT", "BEFORE", "END"]) {
45916            self.previous().text.to_ascii_uppercase()
45917        } else {
45918            return Ok(None);
45919        };
45920
45921        // Expect opening paren and kind (OFFSET, STATEMENT, STREAM, TIMESTAMP, VERSION)
45922        if !self.match_token(TokenType::LParen) {
45923            // Backtrack if not the right pattern
45924            self.current = start_index;
45925            return Ok(None);
45926        }
45927
45928        let kind = if self.match_texts(&["OFFSET", "STATEMENT", "STREAM", "TIMESTAMP", "VERSION"]) {
45929            self.previous().text.to_ascii_uppercase()
45930        } else {
45931            // Backtrack if not the right pattern
45932            self.current = start_index;
45933            return Ok(None);
45934        };
45935
45936        // Expect => and expression
45937        if !self.match_token(TokenType::FArrow) {
45938            self.current = start_index;
45939            return Ok(None);
45940        }
45941
45942        let expression = self.parse_bitwise()?;
45943        if expression.is_none() {
45944            self.current = start_index;
45945            return Ok(None);
45946        }
45947
45948        self.match_token(TokenType::RParen); // Consume closing paren
45949
45950        Ok(Some(Expression::HistoricalData(Box::new(HistoricalData {
45951            this: Box::new(Expression::Identifier(Identifier::new(this))),
45952            kind,
45953            expression: Box::new(expression.unwrap()),
45954        }))))
45955    }
45956
45957    /// parse_id_var - Ported from Python _parse_id_var
45958    /// Parses an identifier or variable (more permissive than parse_identifier)
45959    #[allow(unused_variables, unused_mut)]
45960    pub fn parse_id_var(&mut self) -> Result<Option<Expression>> {
45961        // First try to parse a regular identifier
45962        if let Some(ident) = self.parse_identifier()? {
45963            return Ok(Some(ident));
45964        }
45965
45966        // Try to match Var token type
45967        if self.match_token(TokenType::Var) {
45968            let text = self.previous().text.clone();
45969            return Ok(Some(Expression::Identifier(Identifier {
45970                name: text,
45971                quoted: false,
45972                trailing_comments: Vec::new(),
45973                span: None,
45974            })));
45975        }
45976
45977        // Try to match string as identifier (some dialects allow this)
45978        if self.match_token(TokenType::String) {
45979            let text = self.previous().text.clone();
45980            return Ok(Some(Expression::Identifier(Identifier {
45981                name: text,
45982                quoted: true,
45983                trailing_comments: Vec::new(),
45984                span: None,
45985            })));
45986        }
45987
45988        // Accept keywords as identifiers in some contexts
45989        if self.check(TokenType::Select)
45990            || self.check(TokenType::From)
45991            || self.check(TokenType::Where)
45992            || self.check(TokenType::And)
45993            || self.check(TokenType::Or)
45994            || self.check(TokenType::Not)
45995            || self.check(TokenType::True)
45996            || self.check(TokenType::False)
45997            || self.check(TokenType::Null)
45998        {
45999            // Don't consume keywords as identifiers in parse_id_var
46000            return Ok(None);
46001        }
46002
46003        Ok(None)
46004    }
46005
46006    /// parse_identifier - Parse quoted identifier
46007    /// Python: if self._match(TokenType.IDENTIFIER): return self._identifier_expression(quoted=True)
46008    pub fn parse_identifier(&mut self) -> Result<Option<Expression>> {
46009        // Match quoted identifiers (e.g., "column_name" or `column_name`)
46010        if self.match_token(TokenType::QuotedIdentifier) || self.match_token(TokenType::Identifier)
46011        {
46012            let text = self.previous().text.clone();
46013            let quoted = self.previous().token_type == TokenType::QuotedIdentifier;
46014            return Ok(Some(Expression::Identifier(Identifier {
46015                name: text,
46016                quoted,
46017                trailing_comments: Vec::new(),
46018                span: None,
46019            })));
46020        }
46021        Ok(None)
46022    }
46023
46024    /// Parse IF expression
46025    /// IF(condition, true_value, false_value) - function style
46026    /// IF condition THEN true_value ELSE false_value END - statement style
46027    pub fn parse_if(&mut self) -> Result<Option<Expression>> {
46028        // TSQL/Fabric: IF (cond) BEGIN ... END is a statement, not a function.
46029        // Parse condition, strip outer parens, then capture rest as command.
46030        if matches!(
46031            self.config.dialect,
46032            Some(crate::dialects::DialectType::TSQL) | Some(crate::dialects::DialectType::Fabric)
46033        ) && self.check(TokenType::LParen) {
46034            // Parse the parenthesized condition using balanced paren matching
46035            let cond_start = self.current;
46036            self.skip(); // consume opening (
46037            let mut depth = 1;
46038            while depth > 0 && !self.is_at_end() {
46039                if self.check(TokenType::LParen) {
46040                    depth += 1;
46041                } else if self.check(TokenType::RParen) {
46042                    depth -= 1;
46043                    if depth == 0 {
46044                        break;
46045                    }
46046                }
46047                self.skip();
46048            }
46049            // Extract condition text from source (inside outer parens)
46050            let cond_text = if let Some(ref source) = self.source {
46051                let inner_start = self.tokens[cond_start + 1].span.start;
46052                let inner_end = self.tokens[self.current].span.start;
46053                source[inner_start..inner_end].trim().to_string()
46054            } else {
46055                self.tokens_to_sql(cond_start + 1, self.current)
46056            };
46057            self.skip(); // consume closing )
46058
46059            // Now collect the rest (BEGIN...END) as raw text
46060            let body_start = self.current;
46061            while !self.is_at_end() && !self.check(TokenType::Semicolon) {
46062                self.skip();
46063            }
46064            let body_text = if let Some(ref source) = self.source {
46065                let start_span = self.tokens[body_start].span.start;
46066                let end_span = if self.current > 0 { self.tokens[self.current - 1].span.end } else { start_span };
46067                source[start_span..end_span].trim().to_string()
46068            } else {
46069                self.tokens_to_sql(body_start, self.current)
46070            };
46071            let command_text = format!("IF {} {}", cond_text, body_text);
46072            return Ok(Some(Expression::Command(Box::new(crate::expressions::Command {
46073                this: command_text,
46074            }))));
46075        }
46076
46077        // Function style: IF(cond, true, false)
46078        if self.match_token(TokenType::LParen) {
46079            // ClickHouse: if() with zero args is valid (used in test queries)
46080            if self.check(TokenType::RParen) {
46081                self.skip(); // consume RParen
46082                return Ok(Some(Expression::Function(Box::new(Function {
46083                    name: "IF".to_string(),
46084                    args: vec![],
46085                    distinct: false,
46086                    trailing_comments: Vec::new(),
46087                    use_bracket_syntax: false,
46088                    no_parens: false,
46089                    quoted: false,
46090                    span: None,
46091                    inferred_type: None,
46092                }))));
46093            }
46094            let args = self.parse_expression_list()?;
46095            self.expect(TokenType::RParen)?;
46096
46097            if args.len() == 3 {
46098                return Ok(Some(Expression::IfFunc(Box::new(IfFunc {
46099                    original_name: None,
46100                    condition: args[0].clone(),
46101                    true_value: args[1].clone(),
46102                    false_value: Some(args[2].clone()),
46103                    inferred_type: None,
46104                }))));
46105            } else if args.len() == 2 {
46106                return Ok(Some(Expression::IfFunc(Box::new(IfFunc {
46107                    original_name: None,
46108                    condition: args[0].clone(),
46109                    true_value: args[1].clone(),
46110                    false_value: None,
46111                    inferred_type: None,
46112                }))));
46113            } else if args.len() == 1 {
46114                return Ok(Some(Expression::Function(Box::new(Function {
46115                    name: "IF".to_string(),
46116                    args,
46117                    distinct: false,
46118                    trailing_comments: Vec::new(),
46119                    use_bracket_syntax: false,
46120                    no_parens: false,
46121                    quoted: false,
46122                    span: None,
46123                    inferred_type: None,
46124                }))));
46125            } else {
46126                return Err(self.parse_error("IF function requires 2 or 3 arguments"));
46127            }
46128        }
46129
46130        // TSQL: IF OBJECT_ID(...) IS NOT NULL [BEGIN] DROP TABLE x [; END] -> DROP TABLE IF EXISTS x
46131        if matches!(
46132            self.config.dialect,
46133            Some(crate::dialects::DialectType::TSQL) | Some(crate::dialects::DialectType::Fabric)
46134        ) {
46135            let saved = self.current;
46136            if self.match_text_seq(&["OBJECT_ID"]) {
46137                // Capture the OBJECT_ID arguments text for TSQL round-trip
46138                let object_id_args_text = if self.match_token(TokenType::LParen) {
46139                    let args_start = self.current;
46140                    let args = self.parse_expression_list()?;
46141                    // Reconstruct args text from source
46142                    let args_text = if let Some(ref source) = self.source {
46143                        let start_span = self.tokens[args_start].span.start;
46144                        let end_span = self.tokens[self.current].span.start;
46145                        source[start_span..end_span].trim().to_string()
46146                    } else {
46147                        // Fallback: generate from parsed expressions
46148                        args.iter()
46149                            .map(|a| format!("{:?}", a))
46150                            .collect::<Vec<_>>()
46151                            .join(", ")
46152                    };
46153                    let _ = self.match_token(TokenType::RParen);
46154                    Some(args_text)
46155                } else {
46156                    None
46157                };
46158                if self.match_text_seq(&["IS", "NOT", "NULL"]) {
46159                    // Check for DROP directly or BEGIN ... DROP ... END
46160                    let has_begin = self.match_token(TokenType::Begin);
46161                    if self.check(TokenType::Drop) {
46162                        // Parse DROP TABLE, forcing if_exists = true
46163                        self.skip(); // consume DROP
46164                        if self.match_token(TokenType::Table) {
46165                            // Parse table names
46166                            let mut names = Vec::new();
46167                            loop {
46168                                names.push(self.parse_table_ref()?);
46169                                if !self.match_token(TokenType::Comma) {
46170                                    break;
46171                                }
46172                            }
46173                            // If we had BEGIN, consume optional ; and END
46174                            if has_begin {
46175                                let _ = self.match_token(TokenType::Semicolon);
46176                                let _ = self.match_token(TokenType::End);
46177                            }
46178                            return Ok(Some(Expression::DropTable(Box::new(
46179                                crate::expressions::DropTable {
46180                                    names,
46181                                    if_exists: true,
46182                                    cascade: false,
46183                                    cascade_constraints: false,
46184                                    purge: false,
46185                                    leading_comments: Vec::new(),
46186                                    object_id_args: object_id_args_text,
46187                                },
46188                            ))));
46189                        }
46190                    }
46191                }
46192                // Retreat if pattern didn't match
46193                self.current = saved;
46194            }
46195        }
46196
46197        // Statement style: IF cond THEN true [ELSE false] END/ENDIF
46198        // Use parse_disjunction (parse_or) for condition - same as Python sqlglot
46199        // This ensures we stop at THEN rather than consuming too much
46200        let condition = match self.parse_disjunction()? {
46201            Some(c) => c,
46202            None => return Ok(None),
46203        };
46204
46205        if !self.match_token(TokenType::Then) {
46206            // Not statement style, return as just the expression parsed
46207            return Ok(Some(condition));
46208        }
46209
46210        // Parse true value - use parse_disjunction to stop at ELSE/END
46211        let true_value = match self.parse_disjunction()? {
46212            Some(v) => v,
46213            None => return Err(self.parse_error("Expected expression after THEN")),
46214        };
46215
46216        let false_value = if self.match_token(TokenType::Else) {
46217            match self.parse_disjunction()? {
46218                Some(v) => Some(v),
46219                None => return Err(self.parse_error("Expected expression after ELSE")),
46220            }
46221        } else {
46222            None
46223        };
46224
46225        // Consume END or ENDIF (Exasol tokenizes ENDIF as END)
46226        self.match_token(TokenType::End);
46227
46228        Ok(Some(Expression::IfFunc(Box::new(IfFunc {
46229            original_name: None,
46230            condition,
46231            true_value,
46232            false_value,
46233            inferred_type: None,
46234        }))))
46235    }
46236
46237    /// parse_in - Ported from Python _parse_in
46238    /// Parses IN expression: expr IN (values...) or expr IN (subquery)
46239    /// Can also parse standalone IN list after IN keyword has been matched
46240    #[allow(unused_variables, unused_mut)]
46241    pub fn parse_in(&mut self) -> Result<Option<Expression>> {
46242        // If we're at IN keyword, parse what follows
46243        if self.match_token(TokenType::In) {
46244            return Err(self.parse_error("Expected expression before IN"));
46245        }
46246
46247        // Try to parse as a complete expression: left IN (...)
46248        let saved_pos = self.current;
46249
46250        // Parse the left side expression
46251        match self.parse_bitwise() {
46252            Ok(Some(left_expr)) => {
46253                // Check for optional NOT
46254                let negate = self.match_token(TokenType::Not);
46255
46256                // Expect IN keyword
46257                if self.match_token(TokenType::In) {
46258                    let in_result = self.parse_in_with_expr(Some(left_expr))?;
46259                    return Ok(Some(if negate {
46260                        Expression::Not(Box::new(UnaryOp {
46261                            this: in_result,
46262                            inferred_type: None,
46263                        }))
46264                    } else {
46265                        in_result
46266                    }));
46267                }
46268
46269                // Not an IN expression, restore position
46270                self.current = saved_pos;
46271                Ok(None)
46272            }
46273            Ok(None) => {
46274                self.current = saved_pos;
46275                Ok(None)
46276            }
46277            Err(_) => {
46278                self.current = saved_pos;
46279                Ok(None)
46280            }
46281        }
46282    }
46283
46284    /// parse_index - Implemented from Python _parse_index
46285    /// Calls: parse_index_params, parse_id_var
46286    #[allow(unused_variables, unused_mut)]
46287    pub fn parse_index(&mut self) -> Result<Option<Expression>> {
46288        if self.match_text_seq(&["PRIMARY"]) {
46289            return Ok(Some(Expression::Index(Box::new(Index {
46290                this: None,
46291                table: None,
46292                unique: false,
46293                primary: None,
46294                amp: None,
46295                params: Vec::new(),
46296            }))));
46297        }
46298        if self.match_text_seq(&["AMP"]) {
46299            // Matched: AMP
46300            return Ok(None);
46301        }
46302        Ok(None)
46303    }
46304
46305    /// parse_index_params - Implemented from Python _parse_index_params
46306    /// Calls: parse_where, parse_wrapped_properties, parse_wrapped_id_vars
46307    #[allow(unused_variables, unused_mut)]
46308    pub fn parse_index_params(&mut self) -> Result<Option<Expression>> {
46309        if self.match_text_seq(&["INCLUDE"]) {
46310            return Ok(Some(Expression::IndexParameters(Box::new(
46311                IndexParameters {
46312                    using: None,
46313                    include: None,
46314                    columns: Vec::new(),
46315                    with_storage: None,
46316                    partition_by: None,
46317                    tablespace: None,
46318                    where_: None,
46319                    on: None,
46320                },
46321            ))));
46322        }
46323        if self.match_text_seq(&["USING", "INDEX", "TABLESPACE"]) {
46324            // Matched: USING INDEX TABLESPACE
46325            return Ok(None);
46326        }
46327        Ok(None)
46328    }
46329
46330    /// parse_initcap - Ported from Python _parse_initcap
46331    #[allow(unused_variables, unused_mut)]
46332    /// parse_initcap - Parses INITCAP function
46333    /// Example: INITCAP(str) or INITCAP(str, delimiter)
46334    pub fn parse_initcap(&mut self) -> Result<Option<Expression>> {
46335        // Parse the first argument (string to capitalize)
46336        let args = self.parse_expression_list()?;
46337
46338        if args.is_empty() {
46339            return Ok(None);
46340        }
46341
46342        // Initcap is a UnaryFunc
46343        Ok(Some(Expression::Initcap(Box::new(UnaryFunc::new(
46344            args.into_iter().next().unwrap(),
46345        )))))
46346    }
46347
46348    /// parse_inline - Implemented from Python _parse_inline
46349    #[allow(unused_variables, unused_mut)]
46350    pub fn parse_inline(&mut self) -> Result<Option<Expression>> {
46351        if self.match_text_seq(&["LENGTH"]) {
46352            // Matched: LENGTH
46353            return Ok(None);
46354        }
46355        Ok(None)
46356    }
46357
46358    /// parse_insert_table - Parse table reference for INSERT statement
46359    /// Parses: table_name [schema] [partition] [alias]
46360    /// This method is a simple wrapper around parse_table for INSERT context
46361    #[allow(unused_variables, unused_mut)]
46362    pub fn parse_insert_table(&mut self) -> Result<Option<Expression>> {
46363        // Parse the table reference - parse_table handles aliases
46364        self.parse_table()
46365    }
46366
46367    /// parse_interpolate - Implemented from Python _parse_interpolate
46368    /// Parses INTERPOLATE clause for ClickHouse ORDER BY WITH FILL
46369    pub fn parse_interpolate(&mut self) -> Result<Option<Expression>> {
46370        if !self.match_text_seq(&["INTERPOLATE"]) {
46371            return Ok(None);
46372        }
46373
46374        // Parse wrapped CSV of name-as-expression pairs
46375        if self.match_token(TokenType::LParen) {
46376            let mut expressions = Vec::new();
46377            loop {
46378                if let Some(expr) = self.parse_name_as_expression()? {
46379                    expressions.push(expr);
46380                }
46381                if !self.match_token(TokenType::Comma) {
46382                    break;
46383                }
46384            }
46385            self.match_token(TokenType::RParen);
46386
46387            if expressions.is_empty() {
46388                return Ok(None);
46389            }
46390
46391            return Ok(Some(Expression::Tuple(Box::new(Tuple { expressions }))));
46392        }
46393
46394        Ok(None)
46395    }
46396
46397    /// parse_interval - Creates Interval expression
46398    /// Parses INTERVAL expressions: INTERVAL '1 day', INTERVAL 1 MONTH, etc.
46399    #[allow(unused_variables, unused_mut)]
46400    pub fn parse_interval(&mut self) -> Result<Option<Expression>> {
46401        // Delegate to the existing try_parse_interval method
46402        self.try_parse_interval()
46403    }
46404
46405    /// parse_interval_span - Implemented from Python _parse_interval_span
46406    /// Calls: parse_function
46407    #[allow(unused_variables, unused_mut)]
46408    pub fn parse_interval_span(&mut self) -> Result<Option<Expression>> {
46409        if self.match_text_seq(&["TO"]) {
46410            return Ok(Some(Expression::Var(Box::new(Var {
46411                this: String::new(),
46412            }))));
46413        }
46414        if self.match_text_seq(&["TO"]) {
46415            // Matched: TO
46416            return Ok(None);
46417        }
46418        Ok(None)
46419    }
46420
46421    /// parse_into - Implemented from Python _parse_into
46422    /// Parses: INTO [TEMPORARY] [UNLOGGED] [TABLE] table_name
46423    /// Returns the table expression for the INTO clause
46424    #[allow(unused_variables, unused_mut)]
46425    pub fn parse_into(&mut self) -> Result<Option<Expression>> {
46426        if !self.match_token(TokenType::Into) {
46427            return Ok(None);
46428        }
46429
46430        // Optional TEMPORARY
46431        let _temp = self.match_token(TokenType::Temporary);
46432
46433        // Optional UNLOGGED
46434        let _unlogged = self.match_text_seq(&["UNLOGGED"]);
46435
46436        // Optional TABLE keyword
46437        let _ = self.match_token(TokenType::Table);
46438
46439        // Parse the table name
46440        self.parse_table_parts()
46441    }
46442
46443    /// parse_introducer - Parses MySQL introducer expression (_charset'string')
46444    /// Python: _parse_introducer
46445    /// Format: _charset 'literal'
46446    pub fn parse_introducer(&mut self) -> Result<Option<Expression>> {
46447        // We expect to have already consumed the introducer token (e.g., _utf8)
46448        let token = self.previous().clone();
46449
46450        // Try to parse a primary expression (usually a string literal)
46451        // parse_primary returns Expression (not Option), so we use it directly
46452        let literal = self.parse_primary()?;
46453
46454        // Check if it's a null expression (indicating nothing was parsed)
46455        match &literal {
46456            Expression::Null(_) => {
46457                // Just return as an identifier
46458                Ok(Some(Expression::Identifier(Identifier {
46459                    name: token.text.clone(),
46460                    quoted: false,
46461                    trailing_comments: Vec::new(),
46462                    span: None,
46463                })))
46464            }
46465            _ => Ok(Some(Expression::Introducer(Box::new(Introducer {
46466                this: Box::new(Expression::Identifier(Identifier {
46467                    name: token.text.clone(),
46468                    quoted: false,
46469                    trailing_comments: Vec::new(),
46470                    span: None,
46471                })),
46472                expression: Box::new(literal),
46473            })))),
46474        }
46475    }
46476
46477    /// parse_is - Implemented from Python _parse_is
46478    /// Calls: parse_null, parse_bitwise
46479    #[allow(unused_variables, unused_mut)]
46480    pub fn parse_is(&mut self) -> Result<Option<Expression>> {
46481        if self.match_text_seq(&["DISTINCT", "FROM"]) {
46482            return Ok(Some(Expression::JSON(Box::new(JSON {
46483                this: None,
46484                with_: None,
46485                unique: false,
46486            }))));
46487        }
46488        if self.match_text_seq(&["WITH"]) {
46489            // Matched: WITH
46490            return Ok(None);
46491        }
46492        if self.match_text_seq(&["WITHOUT"]) {
46493            // Matched: WITHOUT
46494            return Ok(None);
46495        }
46496        Ok(None)
46497    }
46498
46499    /// parse_join - Ported from Python _parse_join
46500    /// Parses a single JOIN clause: [method] [side] [kind] JOIN table [ON condition | USING (columns)]
46501    /// Returns the Join wrapped in an Expression, or None if no join is found
46502    #[allow(unused_variables, unused_mut)]
46503    pub fn parse_join(&mut self) -> Result<Option<Expression>> {
46504        // Check for comma-style implicit join
46505        if self.match_token(TokenType::Comma) {
46506            if let Ok(Some(table)) = self.parse_table() {
46507                return Ok(Some(Expression::Join(Box::new(Join {
46508                    this: table,
46509                    on: None,
46510                    using: Vec::new(),
46511                    kind: JoinKind::Implicit,
46512                    use_inner_keyword: false,
46513                    use_outer_keyword: false,
46514                    deferred_condition: false,
46515                    join_hint: None,
46516                    match_condition: None,
46517                    pivots: Vec::new(),
46518                    comments: Vec::new(),
46519                    nesting_group: 0,
46520                    directed: false,
46521                }))));
46522            }
46523            return Ok(None);
46524        }
46525
46526        // Try to parse join kind (INNER, LEFT, RIGHT, FULL, CROSS, etc.)
46527        let saved_pos = self.current;
46528        if let Some((kind, needs_join_keyword, use_inner_keyword, use_outer_keyword, join_hint)) =
46529            self.try_parse_join_kind()
46530        {
46531            // Collect comments from tokens consumed by try_parse_join_kind
46532            let mut join_comments = Vec::new();
46533            for i in saved_pos..self.current {
46534                if i < self.tokens.len() {
46535                    join_comments.extend(self.tokens[i].trailing_comments.iter().cloned());
46536                }
46537            }
46538
46539            // If kind requires JOIN keyword, expect it
46540            if needs_join_keyword && !self.match_token(TokenType::Join) {
46541                self.current = saved_pos;
46542                return Ok(None);
46543            }
46544
46545            // Parse the table being joined
46546            let table = self.parse_table_expression()?;
46547
46548            // Parse ON or USING condition
46549            let (on, using) = if self.match_token(TokenType::On) {
46550                (Some(self.parse_expression()?), Vec::new())
46551            } else if self.match_token(TokenType::Using) {
46552                let has_parens = self.match_token(TokenType::LParen);
46553                // Use parse_using_column_list to handle qualified names like t1.col
46554                let cols = self.parse_using_column_list()?;
46555                if has_parens {
46556                    self.expect(TokenType::RParen)?;
46557                }
46558                (None, cols)
46559            } else {
46560                (None, Vec::new())
46561            };
46562
46563            return Ok(Some(Expression::Join(Box::new(Join {
46564                this: table,
46565                on,
46566                using,
46567                kind,
46568                use_inner_keyword,
46569                use_outer_keyword,
46570                deferred_condition: false,
46571                join_hint,
46572                match_condition: None,
46573                pivots: Vec::new(),
46574                comments: join_comments,
46575                nesting_group: 0,
46576                directed: false,
46577            }))));
46578        }
46579
46580        // Check for CROSS APPLY / OUTER APPLY (SQL Server)
46581        if self.match_text_seq(&["CROSS", "APPLY"]) || self.match_text_seq(&["OUTER", "APPLY"]) {
46582            let is_outer = self.previous().text.eq_ignore_ascii_case("OUTER");
46583            let table = self.parse_table_expression()?;
46584            return Ok(Some(Expression::Join(Box::new(Join {
46585                this: table,
46586                on: None,
46587                using: Vec::new(),
46588                kind: if is_outer {
46589                    JoinKind::Outer
46590                } else {
46591                    JoinKind::Cross
46592                },
46593                use_inner_keyword: false,
46594                use_outer_keyword: is_outer,
46595                deferred_condition: false,
46596                join_hint: None,
46597                match_condition: None,
46598                pivots: Vec::new(),
46599                comments: Vec::new(),
46600                nesting_group: 0,
46601                directed: false,
46602            }))));
46603        }
46604
46605        Ok(None)
46606    }
46607
46608    /// parse_join_hint - Spark/Hive join hints (BROADCAST, MERGE, SHUFFLE_HASH, etc.)
46609    /// Parses: HINT_NAME(table1, table2, ...)
46610    /// hint_name should be the already matched hint keyword (BROADCAST, MAPJOIN, etc.)
46611    #[allow(unused_variables, unused_mut)]
46612    pub fn parse_join_hint(&mut self, hint_name: &str) -> Result<Option<Expression>> {
46613        // Parse comma-separated list of tables
46614        let mut tables = Vec::new();
46615        loop {
46616            if let Some(table) = self.parse_table()? {
46617                tables.push(table);
46618            } else {
46619                break;
46620            }
46621            if !self.match_token(TokenType::Comma) {
46622                break;
46623            }
46624        }
46625
46626        Ok(Some(Expression::JoinHint(Box::new(JoinHint {
46627            this: Box::new(Expression::Identifier(Identifier::new(
46628                hint_name.to_ascii_uppercase(),
46629            ))),
46630            expressions: tables,
46631        }))))
46632    }
46633
46634    /// parse_join_parts - Ported from Python _parse_join_parts
46635    /// Returns (method, side, kind) where each is an optional string
46636    /// method: ASOF, NATURAL, POSITIONAL
46637    /// side: LEFT, RIGHT, FULL
46638    /// kind: ANTI, CROSS, INNER, OUTER, SEMI
46639    pub fn parse_join_parts(&mut self) -> (Option<String>, Option<String>, Option<String>) {
46640        // Parse join method (ASOF, NATURAL, POSITIONAL)
46641        let method = if self.match_texts(&["ASOF", "NATURAL", "POSITIONAL"]) {
46642            Some(self.previous().text.to_ascii_uppercase())
46643        } else {
46644            None
46645        };
46646
46647        // Parse join side (LEFT, RIGHT, FULL)
46648        let side = if self.match_texts(&["LEFT", "RIGHT", "FULL"]) {
46649            Some(self.previous().text.to_ascii_uppercase())
46650        } else {
46651            None
46652        };
46653
46654        // Parse join kind (ANTI, CROSS, INNER, OUTER, SEMI)
46655        let kind = if self.match_texts(&["ANTI", "CROSS", "INNER", "OUTER", "SEMI"]) {
46656            Some(self.previous().text.to_ascii_uppercase())
46657        } else if self.match_token(TokenType::StraightJoin) {
46658            Some("STRAIGHT_JOIN".to_string())
46659        } else {
46660            None
46661        };
46662
46663        (method, side, kind)
46664    }
46665
46666    /// parse_journal - Parses JOURNAL property (Teradata)
46667    /// Python: _parse_journal
46668    /// Creates a JournalProperty expression
46669    pub fn parse_journal(&mut self) -> Result<Option<Expression>> {
46670        self.parse_journal_impl(false, false, false, false, false)
46671    }
46672
46673    /// Implementation of parse_journal with options
46674    pub fn parse_journal_impl(
46675        &mut self,
46676        no: bool,
46677        dual: bool,
46678        before: bool,
46679        local: bool,
46680        after: bool,
46681    ) -> Result<Option<Expression>> {
46682        Ok(Some(Expression::JournalProperty(Box::new(
46683            JournalProperty {
46684                no: if no {
46685                    Some(Box::new(Expression::Boolean(BooleanLiteral {
46686                        value: true,
46687                    })))
46688                } else {
46689                    None
46690                },
46691                dual: if dual {
46692                    Some(Box::new(Expression::Boolean(BooleanLiteral {
46693                        value: true,
46694                    })))
46695                } else {
46696                    None
46697                },
46698                before: if before {
46699                    Some(Box::new(Expression::Boolean(BooleanLiteral {
46700                        value: true,
46701                    })))
46702                } else {
46703                    None
46704                },
46705                local: if local {
46706                    Some(Box::new(Expression::Boolean(BooleanLiteral {
46707                        value: true,
46708                    })))
46709                } else {
46710                    None
46711                },
46712                after: if after {
46713                    Some(Box::new(Expression::Boolean(BooleanLiteral {
46714                        value: true,
46715                    })))
46716                } else {
46717                    None
46718                },
46719            },
46720        ))))
46721    }
46722
46723    /// parse_json_column_def - Implemented from Python _parse_json_column_def
46724    /// Calls: parse_string, parse_json_schema, parse_id_var
46725    #[allow(unused_variables, unused_mut)]
46726    pub fn parse_json_column_def(&mut self) -> Result<Option<Expression>> {
46727        if self.match_text_seq(&["NESTED"]) {
46728            return Ok(Some(Expression::JSONColumnDef(Box::new(JSONColumnDef {
46729                this: None,
46730                kind: None,
46731                path: None,
46732                nested_schema: None,
46733                ordinality: None,
46734            }))));
46735        }
46736        if self.match_text_seq(&["PATH"]) {
46737            // Matched: PATH
46738            return Ok(None);
46739        }
46740        Ok(None)
46741    }
46742
46743    /// parse_json_key_value - Implemented from Python _parse_json_key_value
46744    #[allow(unused_variables, unused_mut)]
46745    /// parse_json_key_value - Parses a JSON key-value pair
46746    /// Python: _parse_json_key_value
46747    /// Format: [KEY] key [: | VALUE] value
46748    pub fn parse_json_key_value(&mut self) -> Result<Option<Expression>> {
46749        // Optional KEY keyword
46750        self.match_text_seq(&["KEY"]);
46751
46752        // Parse the key expression
46753        let key = self.parse_column()?;
46754
46755        // Match separator (colon, comma, or VALUE keyword)
46756        let _ = self.match_token(TokenType::Colon)
46757            || self.match_token(TokenType::Comma)
46758            || self.match_text_seq(&["VALUE"]);
46759
46760        // Optional VALUE keyword
46761        self.match_text_seq(&["VALUE"]);
46762
46763        // Parse the value expression
46764        let value = self.parse_bitwise()?;
46765
46766        // If neither key nor value, return None
46767        match (key, value) {
46768            (None, None) => Ok(None),
46769            (Some(k), None) => Ok(Some(Expression::JSONKeyValue(Box::new(JSONKeyValue {
46770                this: Box::new(k),
46771                expression: Box::new(Expression::Null(Null)),
46772            })))),
46773            (None, Some(v)) => Ok(Some(Expression::JSONKeyValue(Box::new(JSONKeyValue {
46774                this: Box::new(Expression::Null(Null)),
46775                expression: Box::new(v),
46776            })))),
46777            (Some(k), Some(v)) => Ok(Some(Expression::JSONKeyValue(Box::new(JSONKeyValue {
46778                this: Box::new(k),
46779                expression: Box::new(v),
46780            })))),
46781        }
46782    }
46783
46784    /// parse_json_object - Parses JSON_OBJECT function
46785    /// Python: _parse_json_object
46786    /// Handles both JSON_OBJECT and JSON_OBJECTAGG
46787    pub fn parse_json_object(&mut self) -> Result<Option<Expression>> {
46788        self.parse_json_object_impl(false)
46789    }
46790
46791    /// Implementation of JSON object parsing with aggregate flag
46792    pub fn parse_json_object_impl(&mut self, agg: bool) -> Result<Option<Expression>> {
46793        // Try to parse a star expression
46794        let star = self.parse_star()?;
46795
46796        // Parse expressions: either star or comma-separated key-value pairs
46797        let expressions = if let Some(star_expr) = star {
46798            vec![star_expr]
46799        } else {
46800            // Parse comma-separated JSON key-value pairs
46801            let mut exprs = Vec::new();
46802            loop {
46803                if let Some(kv) = self.parse_json_key_value()? {
46804                    // Wrap with FORMAT JSON if specified
46805                    if self.match_text_seq(&["FORMAT", "JSON"]) {
46806                        exprs.push(Expression::JSONFormat(Box::new(JSONFormat {
46807                            this: Some(Box::new(kv)),
46808                            options: Vec::new(),
46809                            is_json: None,
46810                            to_json: None,
46811                        })));
46812                    } else {
46813                        exprs.push(kv);
46814                    }
46815                } else {
46816                    break;
46817                }
46818                if !self.match_token(TokenType::Comma) {
46819                    break;
46820                }
46821            }
46822            exprs
46823        };
46824
46825        // Parse NULL handling: NULL ON NULL or ABSENT ON NULL
46826        let null_handling = self.parse_json_on_null_handling()?;
46827
46828        // Parse UNIQUE KEYS option
46829        let unique_keys = if self.match_text_seq(&["WITH", "UNIQUE"]) {
46830            self.match_text_seq(&["KEYS"]);
46831            Some(Box::new(Expression::Boolean(BooleanLiteral {
46832                value: true,
46833            })))
46834        } else if self.match_text_seq(&["WITHOUT", "UNIQUE"]) {
46835            self.match_text_seq(&["KEYS"]);
46836            Some(Box::new(Expression::Boolean(BooleanLiteral {
46837                value: false,
46838            })))
46839        } else {
46840            None
46841        };
46842
46843        // Consume optional KEYS keyword
46844        self.match_text_seq(&["KEYS"]);
46845
46846        // Parse RETURNING clause
46847        let return_type = if self.match_text_seq(&["RETURNING"]) {
46848            let type_expr = self.parse_type()?;
46849            // Wrap with FORMAT JSON if specified
46850            if self.match_text_seq(&["FORMAT", "JSON"]) {
46851                type_expr.map(|t| {
46852                    Box::new(Expression::JSONFormat(Box::new(JSONFormat {
46853                        this: Some(Box::new(t)),
46854                        options: Vec::new(),
46855                        is_json: None,
46856                        to_json: None,
46857                    })))
46858                })
46859            } else {
46860                type_expr.map(Box::new)
46861            }
46862        } else {
46863            None
46864        };
46865
46866        // Parse ENCODING option
46867        let encoding = if self.match_text_seq(&["ENCODING"]) {
46868            self.parse_var()?.map(Box::new)
46869        } else {
46870            None
46871        };
46872
46873        if agg {
46874            Ok(Some(Expression::JSONObjectAgg(Box::new(JSONObjectAgg {
46875                expressions,
46876                null_handling,
46877                unique_keys,
46878                return_type,
46879                encoding,
46880            }))))
46881        } else {
46882            Ok(Some(Expression::JSONObject(Box::new(JSONObject {
46883                expressions,
46884                null_handling,
46885                unique_keys,
46886                return_type,
46887                encoding,
46888            }))))
46889        }
46890    }
46891
46892    /// Parse JSON NULL handling clause: NULL ON NULL or ABSENT ON NULL
46893    fn parse_json_on_null_handling(&mut self) -> Result<Option<Box<Expression>>> {
46894        if self.match_text_seq(&["NULL", "ON", "NULL"]) {
46895            Ok(Some(Box::new(Expression::Var(Box::new(Var {
46896                this: "NULL ON NULL".to_string(),
46897            })))))
46898        } else if self.match_text_seq(&["ABSENT", "ON", "NULL"]) {
46899            Ok(Some(Box::new(Expression::Var(Box::new(Var {
46900                this: "ABSENT ON NULL".to_string(),
46901            })))))
46902        } else {
46903            Ok(None)
46904        }
46905    }
46906
46907    /// parse_json_schema - Implemented from Python _parse_json_schema
46908    #[allow(unused_variables, unused_mut)]
46909    pub fn parse_json_schema(&mut self) -> Result<Option<Expression>> {
46910        if self.match_text_seq(&["COLUMNS"]) {
46911            return Ok(Some(Expression::JSONSchema(Box::new(JSONSchema {
46912                expressions: Vec::new(),
46913            }))));
46914        }
46915        Ok(None)
46916    }
46917
46918    /// Parse JSON_TABLE COLUMNS clause: COLUMNS (column_def, column_def, ...) or COLUMNS column_def
46919    /// Column definitions can be:
46920    /// - name type PATH 'json_path'
46921    /// - name FOR ORDINALITY
46922    /// - NESTED [PATH] 'json_path' COLUMNS (...)
46923    pub fn parse_json_table_columns(&mut self) -> Result<Option<Expression>> {
46924        if !self.match_text_seq(&["COLUMNS"]) {
46925            return Ok(None);
46926        }
46927
46928        // Check for opening paren - Oracle supports both COLUMNS(...) and COLUMNS col PATH '...'
46929        let has_parens = self.match_token(TokenType::LParen);
46930
46931        let mut columns = Vec::new();
46932
46933        // Parse column definitions
46934        if has_parens {
46935            // COLUMNS(col1, col2, ...)
46936            if !self.check(TokenType::RParen) {
46937                loop {
46938                    if let Some(col_def) = self.parse_json_table_column_def()? {
46939                        columns.push(col_def);
46940                    }
46941                    if !self.match_token(TokenType::Comma) {
46942                        break;
46943                    }
46944                }
46945            }
46946            // Expect closing paren for COLUMNS(...)
46947            self.expect(TokenType::RParen)?;
46948        } else {
46949            // COLUMNS col PATH '...' (single column without parens)
46950            if let Some(col_def) = self.parse_json_table_column_def()? {
46951                columns.push(col_def);
46952            }
46953        }
46954
46955        Ok(Some(Expression::JSONSchema(Box::new(JSONSchema {
46956            expressions: columns,
46957        }))))
46958    }
46959
46960    /// Parse a single JSON_TABLE column definition
46961    /// Formats:
46962    /// - name [FOR ORDINALITY] [type] [PATH 'path']
46963    /// - NESTED [PATH] 'path' COLUMNS (...)
46964    pub fn parse_json_table_column_def(&mut self) -> Result<Option<Expression>> {
46965        // Check for NESTED column
46966        if self.match_text_seq(&["NESTED"]) {
46967            // NESTED [PATH] 'json_path' COLUMNS (...)
46968            self.match_text_seq(&["PATH"]); // Optional PATH keyword
46969            let path = self.parse_string()?;
46970            let nested_schema = self.parse_json_table_columns()?;
46971
46972            return Ok(Some(Expression::JSONColumnDef(Box::new(JSONColumnDef {
46973                this: None,
46974                kind: None,
46975                path: path.map(Box::new),
46976                nested_schema: nested_schema.map(Box::new),
46977                ordinality: None,
46978            }))));
46979        }
46980
46981        // Regular column: name [FOR ORDINALITY] [type] [PATH 'path']
46982        let name = self.parse_id_var()?;
46983        if name.is_none() {
46984            return Ok(None);
46985        }
46986
46987        // Check for FOR ORDINALITY
46988        let ordinality = if self.match_text_seq(&["FOR", "ORDINALITY"]) {
46989            Some(Box::new(Expression::Boolean(BooleanLiteral {
46990                value: true,
46991            })))
46992        } else {
46993            None
46994        };
46995
46996        // Parse data type (if not FOR ORDINALITY, type is expected)
46997        let kind = if ordinality.is_none() {
46998            // Try to parse a data type
46999            let data_type = self.parse_data_type_optional()?;
47000            data_type.map(|dt| self.data_type_to_string(&dt))
47001        } else {
47002            None
47003        };
47004
47005        // Parse PATH 'json_path'
47006        let path = if self.match_text_seq(&["PATH"]) {
47007            self.parse_string()?
47008        } else {
47009            None
47010        };
47011
47012        Ok(Some(Expression::JSONColumnDef(Box::new(JSONColumnDef {
47013            this: name.map(Box::new),
47014            kind,
47015            path: path.map(Box::new),
47016            nested_schema: None,
47017            ordinality,
47018        }))))
47019    }
47020
47021    /// Parse JSON_TABLE function
47022    /// JSON_TABLE(expr, path COLUMNS (...)) [ON ERROR ...] [ON EMPTY ...]
47023    pub fn parse_json_table(&mut self) -> Result<Option<Expression>> {
47024        // Parse the JSON expression
47025        let this = self.parse_expression()?;
47026
47027        // Optional path after comma
47028        let path = if self.match_token(TokenType::Comma) {
47029            if let Some(s) = self.parse_string()? {
47030                Some(Box::new(s))
47031            } else {
47032                None
47033            }
47034        } else {
47035            None
47036        };
47037
47038        // Parse error handling: ON ERROR NULL or ON ERROR ERROR
47039        let error_handling = if self.match_text_seq(&["ON", "ERROR"]) {
47040            if self.match_text_seq(&["NULL"]) {
47041                Some(Box::new(Expression::Var(Box::new(Var {
47042                    this: "NULL".to_string(),
47043                }))))
47044            } else if self.match_text_seq(&["ERROR"]) {
47045                Some(Box::new(Expression::Var(Box::new(Var {
47046                    this: "ERROR".to_string(),
47047                }))))
47048            } else {
47049                None
47050            }
47051        } else {
47052            None
47053        };
47054
47055        // Parse empty handling: ON EMPTY NULL or ON EMPTY ERROR
47056        let empty_handling = if self.match_text_seq(&["ON", "EMPTY"]) {
47057            if self.match_text_seq(&["NULL"]) {
47058                Some(Box::new(Expression::Var(Box::new(Var {
47059                    this: "NULL".to_string(),
47060                }))))
47061            } else if self.match_text_seq(&["ERROR"]) {
47062                Some(Box::new(Expression::Var(Box::new(Var {
47063                    this: "ERROR".to_string(),
47064                }))))
47065            } else {
47066                None
47067            }
47068        } else {
47069            None
47070        };
47071
47072        // Parse COLUMNS clause
47073        let schema = self.parse_json_schema()?;
47074
47075        Ok(Some(Expression::JSONTable(Box::new(JSONTable {
47076            this: Box::new(this),
47077            schema: schema.map(Box::new),
47078            path,
47079            error_handling,
47080            empty_handling,
47081        }))))
47082    }
47083
47084    /// parse_json_value - Ported from Python _parse_json_value
47085    #[allow(unused_variables, unused_mut)]
47086    /// parse_json_value - Parses JSON_VALUE function
47087    /// Example: JSON_VALUE(json, '$.path' RETURNING type)
47088    pub fn parse_json_value(&mut self) -> Result<Option<Expression>> {
47089        // Parse the JSON expression
47090        let this = self.parse_expression()?;
47091
47092        // Parse path (after comma)
47093        self.match_token(TokenType::Comma);
47094        let path = self.parse_expression()?;
47095
47096        // Parse optional RETURNING type
47097        let returning = if self.match_token(TokenType::Returning) {
47098            Some(Box::new(self.parse_expression()?))
47099        } else {
47100            None
47101        };
47102
47103        // Parse optional ON condition (ON ERROR, ON EMPTY)
47104        let on_condition = if self.check(TokenType::On) {
47105            self.parse_on_condition()?
47106        } else {
47107            None
47108        };
47109
47110        Ok(Some(Expression::JSONValue(Box::new(JSONValue {
47111            this: Box::new(this),
47112            path: Some(Box::new(path)),
47113            returning,
47114            on_condition: on_condition.map(Box::new),
47115        }))))
47116    }
47117
47118    /// parse_key_constraint_options - Implemented from Python _parse_key_constraint_options
47119    #[allow(unused_variables, unused_mut)]
47120    pub fn parse_key_constraint_options(&mut self) -> Result<Option<Expression>> {
47121        if self.match_text_seq(&["NO", "ACTION"]) {
47122            // Matched: NO ACTION
47123            return Ok(None);
47124        }
47125        if self.match_text_seq(&["CASCADE"]) {
47126            // Matched: CASCADE
47127            return Ok(None);
47128        }
47129        if self.match_text_seq(&["RESTRICT"]) {
47130            // Matched: RESTRICT
47131            return Ok(None);
47132        }
47133        Ok(None)
47134    }
47135
47136    /// parse_lambda - Ported from Python _parse_lambda
47137    /// Parses lambda expressions: x -> x + 1 or (x, y) -> x + y
47138    /// Also supports DuckDB syntax: LAMBDA x : x + 1
47139    #[allow(unused_variables, unused_mut)]
47140    pub fn parse_lambda(&mut self) -> Result<Option<Expression>> {
47141        let start_index = self.current;
47142
47143        // Check for DuckDB's LAMBDA keyword syntax: LAMBDA x : expr
47144        // ClickHouse doesn't use LAMBDA keyword — lambda is just a function name there
47145        if !matches!(
47146            self.config.dialect,
47147            Some(crate::dialects::DialectType::ClickHouse)
47148        ) && self.match_token(TokenType::Lambda)
47149        {
47150            // Parse lambda parameters (comma-separated identifiers)
47151            let mut params = Vec::new();
47152            loop {
47153                // Use is_identifier_token which handles Identifier, QuotedIdentifier, and Var
47154                if self.is_identifier_token() {
47155                    let token = self.advance();
47156                    let quoted = token.token_type == TokenType::QuotedIdentifier;
47157                    params.push(Identifier {
47158                        name: token.text,
47159                        quoted,
47160                        trailing_comments: Vec::new(),
47161                        span: None,
47162                    });
47163                } else {
47164                    break;
47165                }
47166                if !self.match_token(TokenType::Comma) {
47167                    break;
47168                }
47169            }
47170
47171            // Must have at least one parameter
47172            if params.is_empty() {
47173                return Err(self.parse_error("LAMBDA requires at least one parameter"));
47174            }
47175
47176            // Expect colon separator
47177            if !self.match_token(TokenType::Colon) {
47178                return Err(self.parse_error("Expected ':' after LAMBDA parameters"));
47179            }
47180
47181            let body = self.parse_expression()?;
47182            return Ok(Some(Expression::Lambda(Box::new(LambdaExpr {
47183                parameters: params,
47184                body,
47185                colon: true,
47186                parameter_types: Vec::new(),
47187            }))));
47188        }
47189
47190        // Try to parse lambda parameters
47191        let parameters = if self.match_token(TokenType::LParen) {
47192            // Parenthesized parameters: (x, y) -> ...
47193            let mut params = Vec::new();
47194            if !self.check(TokenType::RParen) {
47195                loop {
47196                    if let Some(ident) = self.parse_identifier()? {
47197                        if let Expression::Identifier(id) = ident {
47198                            params.push(id);
47199                        }
47200                    }
47201                    if !self.match_token(TokenType::Comma) {
47202                        break;
47203                    }
47204                }
47205            }
47206            if !self.match_token(TokenType::RParen) {
47207                // Not a lambda, retreat
47208                self.current = start_index;
47209                return Ok(None);
47210            }
47211            params
47212        } else {
47213            // Single parameter: x -> ...
47214            if let Some(ident) = self.parse_identifier()? {
47215                if let Expression::Identifier(id) = ident {
47216                    vec![id]
47217                } else {
47218                    self.current = start_index;
47219                    return Ok(None);
47220                }
47221            } else {
47222                return Ok(None);
47223            }
47224        };
47225
47226        // Check for arrow operator
47227        if self.match_token(TokenType::Arrow) || self.match_token(TokenType::FArrow) {
47228            // Parse lambda body
47229            let body = self.parse_expression()?;
47230            Ok(Some(Expression::Lambda(Box::new(LambdaExpr {
47231                parameters,
47232                body,
47233                colon: false,
47234                parameter_types: Vec::new(),
47235            }))))
47236        } else {
47237            // Not a lambda, retreat
47238            self.current = start_index;
47239            Ok(None)
47240        }
47241    }
47242
47243    /// parse_lambda_arg - Delegates to parse_id_var
47244    #[allow(unused_variables, unused_mut)]
47245    pub fn parse_lambda_arg(&mut self) -> Result<Option<Expression>> {
47246        self.parse_id_var()
47247    }
47248
47249    /// parse_lateral - Parse LATERAL subquery or table function
47250    /// Python: if self._match(TokenType.LATERAL): return exp.Lateral(this=..., view=..., outer=...)
47251    pub fn parse_lateral(&mut self) -> Result<Option<Expression>> {
47252        // Check for CROSS APPLY / OUTER APPLY (handled by join parsing in try_parse_join_kind)
47253        // This method focuses on LATERAL keyword parsing
47254
47255        if !self.match_token(TokenType::Lateral) {
47256            return Ok(None);
47257        }
47258
47259        // Check for LATERAL VIEW (Hive/Spark syntax)
47260        let view = self.match_token(TokenType::View);
47261        let outer = if view {
47262            self.match_token(TokenType::Outer)
47263        } else {
47264            false
47265        };
47266
47267        // Parse the lateral expression (subquery, function call, or table reference)
47268        let this = if self.check(TokenType::LParen) {
47269            // Could be a subquery: LATERAL (SELECT ...)
47270            self.expect(TokenType::LParen)?;
47271            let inner = self.parse_statement()?;
47272            self.expect(TokenType::RParen)?;
47273            inner
47274        } else {
47275            // Could be a function or table reference: LATERAL unnest(...)
47276            self.parse_primary()?
47277        };
47278
47279        // Parse optional alias
47280        let alias = if self.match_token(TokenType::As) {
47281            Some(self.expect_identifier()?)
47282        } else if self.check(TokenType::Identifier) && !self.check_keyword() {
47283            Some(self.expect_identifier()?)
47284        } else {
47285            None
47286        };
47287
47288        // Parse optional column aliases: AS alias(col1, col2, ...)
47289        let column_aliases = if alias.is_some() && self.match_token(TokenType::LParen) {
47290            let mut cols = Vec::new();
47291            loop {
47292                if self.check(TokenType::RParen) {
47293                    break;
47294                }
47295                let col = self.expect_identifier()?;
47296                cols.push(col);
47297                if !self.match_token(TokenType::Comma) {
47298                    break;
47299                }
47300            }
47301            self.expect(TokenType::RParen)?;
47302            cols
47303        } else {
47304            Vec::new()
47305        };
47306
47307        Ok(Some(Expression::Lateral(Box::new(Lateral {
47308            this: Box::new(this),
47309            view: if view {
47310                Some(Box::new(Expression::Boolean(BooleanLiteral {
47311                    value: true,
47312                })))
47313            } else {
47314                None
47315            },
47316            outer: if outer {
47317                Some(Box::new(Expression::Boolean(BooleanLiteral {
47318                    value: true,
47319                })))
47320            } else {
47321                None
47322            },
47323            alias,
47324            alias_quoted: false,
47325            cross_apply: None,
47326            ordinality: None,
47327            column_aliases,
47328        }))))
47329    }
47330
47331    /// parse_limit - Parse LIMIT clause
47332    /// Python: if self._match(TokenType.LIMIT): return exp.Limit(this=self._parse_term())
47333    pub fn parse_limit(&mut self) -> Result<Option<Expression>> {
47334        if !self.match_token(TokenType::Limit) {
47335            return Ok(None);
47336        }
47337        // Parse the limit expression (usually a number)
47338        let limit_expr = self.parse_expression()?;
47339        Ok(Some(Expression::Limit(Box::new(Limit {
47340            this: limit_expr,
47341            percent: false,
47342            comments: Vec::new(),
47343        }))))
47344    }
47345
47346    /// parse_limit_by - Implemented from Python _parse_limit_by
47347    #[allow(unused_variables, unused_mut)]
47348    pub fn parse_limit_by(&mut self) -> Result<Option<Expression>> {
47349        if self.match_text_seq(&["BY"]) {
47350            // Matched: BY
47351            return Ok(None);
47352        }
47353        Ok(None)
47354    }
47355
47356    /// parse_limit_options - Implemented from Python _parse_limit_options
47357    #[allow(unused_variables, unused_mut)]
47358    pub fn parse_limit_options(&mut self) -> Result<Option<Expression>> {
47359        if self.match_text_seq(&["ONLY"]) {
47360            return Ok(Some(Expression::LimitOptions(Box::new(LimitOptions {
47361                percent: None,
47362                rows: None,
47363                with_ties: None,
47364            }))));
47365        }
47366        if self.match_text_seq(&["WITH", "TIES"]) {
47367            // Matched: WITH TIES
47368            return Ok(None);
47369        }
47370        Ok(None)
47371    }
47372
47373    /// parse_load - Implemented from Python _parse_load
47374    #[allow(unused_variables, unused_mut)]
47375    pub fn parse_load(&mut self) -> Result<Option<Expression>> {
47376        if self.match_text_seq(&["DATA"]) {
47377            return Ok(Some(Expression::Command(Box::new(Command {
47378                this: String::new(),
47379            }))));
47380        }
47381        if self.match_text_seq(&["LOCAL"]) {
47382            // Matched: LOCAL
47383            return Ok(None);
47384        }
47385        Ok(None)
47386    }
47387
47388    /// parse_locking - Implemented from Python _parse_locking
47389    /// Calls: parse_table_parts
47390    #[allow(unused_variables, unused_mut)]
47391    pub fn parse_locking(&mut self) -> Result<Option<Expression>> {
47392        let kind = if self.match_token(TokenType::Table) {
47393            Some("TABLE")
47394        } else if self.match_token(TokenType::View) {
47395            Some("VIEW")
47396        } else if self.match_token(TokenType::Row) {
47397            Some("ROW")
47398        } else if self.match_token(TokenType::Database) || self.match_identifier("DATABASE") {
47399            Some("DATABASE")
47400        } else {
47401            None
47402        };
47403
47404        let kind = match kind {
47405            Some(k) => k.to_string(),
47406            None => return Ok(None),
47407        };
47408
47409        let this = if matches!(kind.as_str(), "DATABASE" | "TABLE" | "VIEW") {
47410            self.parse_table_parts()?
47411        } else {
47412            None
47413        };
47414
47415        let for_or_in = if self.match_token(TokenType::For) {
47416            Some("FOR")
47417        } else if self.match_token(TokenType::In) {
47418            Some("IN")
47419        } else {
47420            None
47421        };
47422
47423        let lock_type = if self.match_identifier("ACCESS") {
47424            Some("ACCESS")
47425        } else if self.match_texts(&["EXCL", "EXCLUSIVE"]) {
47426            Some("EXCLUSIVE")
47427        } else if self.match_identifier("SHARE") {
47428            Some("SHARE")
47429        } else if self.match_identifier("READ") {
47430            Some("READ")
47431        } else if self.match_identifier("WRITE") {
47432            Some("WRITE")
47433        } else if self.match_identifier("CHECKSUM") {
47434            Some("CHECKSUM")
47435        } else {
47436            None
47437        };
47438
47439        let override_ = if self.match_identifier("OVERRIDE") {
47440            Some(Box::new(Expression::Boolean(BooleanLiteral {
47441                value: true,
47442            })))
47443        } else {
47444            None
47445        };
47446
47447        Ok(Some(Expression::LockingProperty(Box::new(
47448            LockingProperty {
47449                this: this.map(Box::new),
47450                kind,
47451                for_or_in: for_or_in.map(|v| {
47452                    Box::new(Expression::Var(Box::new(Var {
47453                        this: v.to_string(),
47454                    })))
47455                }),
47456                lock_type: lock_type.map(|v| {
47457                    Box::new(Expression::Var(Box::new(Var {
47458                        this: v.to_string(),
47459                    })))
47460                }),
47461                override_,
47462            },
47463        ))))
47464    }
47465
47466    /// Parse Teradata LOCKING statement: LOCKING <property> SELECT ...
47467    fn parse_locking_statement(&mut self) -> Result<Expression> {
47468        self.expect(TokenType::Lock)?;
47469        let locking = self
47470            .parse_locking()?
47471            .ok_or_else(|| self.parse_error("Expected LOCKING clause"))?;
47472        let query = if self.check(TokenType::With) {
47473            self.parse_statement()?
47474        } else {
47475            self.parse_select()?
47476        };
47477        Ok(Expression::LockingStatement(Box::new(LockingStatement {
47478            this: Box::new(locking),
47479            expression: Box::new(query),
47480        })))
47481    }
47482
47483    /// parse_log - Parses LOG property (Teradata)
47484    /// Python: _parse_log
47485    /// Creates a LogProperty expression
47486    pub fn parse_log(&mut self) -> Result<Option<Expression>> {
47487        self.parse_log_impl(false)
47488    }
47489
47490    /// Implementation of parse_log with no flag
47491    pub fn parse_log_impl(&mut self, no: bool) -> Result<Option<Expression>> {
47492        Ok(Some(Expression::LogProperty(Box::new(LogProperty {
47493            no: if no {
47494                Some(Box::new(Expression::Boolean(BooleanLiteral {
47495                    value: true,
47496                })))
47497            } else {
47498                None
47499            },
47500        }))))
47501    }
47502
47503    /// parse_match_against - Parses MATCH(columns) AGAINST(pattern)
47504    /// Python: parser.py:7125-7153
47505    #[allow(unused_variables, unused_mut)]
47506    pub fn parse_match_against(&mut self) -> Result<Option<Expression>> {
47507        // Parse column expressions or TABLE syntax
47508        let expressions = if self.match_text_seq(&["TABLE"]) {
47509            // SingleStore TABLE syntax
47510            if let Some(table) = self.parse_table()? {
47511                vec![table]
47512            } else {
47513                Vec::new()
47514            }
47515        } else {
47516            // Regular column list
47517            let mut cols = Vec::new();
47518            loop {
47519                if let Some(col) = self.parse_column()? {
47520                    cols.push(col);
47521                }
47522                if !self.match_token(TokenType::Comma) {
47523                    break;
47524                }
47525            }
47526            cols
47527        };
47528
47529        // Match ) AGAINST (
47530        self.match_text_seq(&[")", "AGAINST", "("]);
47531
47532        // Parse the search pattern
47533        let this = self.parse_string()?;
47534
47535        // Parse modifier
47536        let modifier = if self.match_text_seq(&["IN", "NATURAL", "LANGUAGE", "MODE"]) {
47537            if self.match_text_seq(&["WITH", "QUERY", "EXPANSION"]) {
47538                Some(Box::new(Expression::Var(Box::new(Var {
47539                    this: "IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION".to_string(),
47540                }))))
47541            } else {
47542                Some(Box::new(Expression::Var(Box::new(Var {
47543                    this: "IN NATURAL LANGUAGE MODE".to_string(),
47544                }))))
47545            }
47546        } else if self.match_text_seq(&["IN", "BOOLEAN", "MODE"]) {
47547            Some(Box::new(Expression::Var(Box::new(Var {
47548                this: "IN BOOLEAN MODE".to_string(),
47549            }))))
47550        } else if self.match_text_seq(&["WITH", "QUERY", "EXPANSION"]) {
47551            Some(Box::new(Expression::Var(Box::new(Var {
47552                this: "WITH QUERY EXPANSION".to_string(),
47553            }))))
47554        } else {
47555            None
47556        };
47557
47558        match this {
47559            Some(t) => Ok(Some(Expression::MatchAgainst(Box::new(MatchAgainst {
47560                this: Box::new(t),
47561                expressions,
47562                modifier,
47563            })))),
47564            None => Ok(None),
47565        }
47566    }
47567
47568    /// parse_match_recognize_measure - Implemented from Python _parse_match_recognize_measure
47569    /// Parses a MEASURES expression in MATCH_RECOGNIZE: [FINAL|RUNNING] expression
47570    pub fn parse_match_recognize_measure(&mut self) -> Result<Option<Expression>> {
47571        // Check for optional FINAL or RUNNING keyword
47572        let window_frame = if self.match_texts(&["FINAL", "RUNNING"]) {
47573            let text = self.previous().text.to_ascii_uppercase();
47574            Some(if text == "FINAL" {
47575                MatchRecognizeSemantics::Final
47576            } else {
47577                MatchRecognizeSemantics::Running
47578            })
47579        } else {
47580            None
47581        };
47582
47583        // Parse the expression
47584        let this = self.parse_expression()?;
47585
47586        Ok(Some(Expression::MatchRecognizeMeasure(Box::new(
47587            MatchRecognizeMeasure { this, window_frame },
47588        ))))
47589    }
47590
47591    /// parse_max_min_by - MAX_BY / MIN_BY / ARG_MAX / ARG_MIN aggregate functions
47592    /// Parses: MAX_BY(value, key [, n]) or MIN_BY(value, key [, n])
47593    /// is_max: true for MAX_BY/ARG_MAX, false for MIN_BY/ARG_MIN
47594    #[allow(unused_variables, unused_mut)]
47595    pub fn parse_max_min_by(&mut self, is_max: bool) -> Result<Option<Expression>> {
47596        let mut args = Vec::new();
47597
47598        // Handle optional DISTINCT
47599        let distinct = if self.match_token(TokenType::Distinct) {
47600            let lambda_expr = self.parse_lambda()?;
47601            if let Some(expr) = lambda_expr {
47602                args.push(expr);
47603            }
47604            self.match_token(TokenType::Comma);
47605            true
47606        } else {
47607            false
47608        };
47609
47610        // Parse remaining arguments
47611        loop {
47612            if let Some(arg) = self.parse_lambda()? {
47613                args.push(arg);
47614            } else {
47615                break;
47616            }
47617            if !self.match_token(TokenType::Comma) {
47618                break;
47619            }
47620        }
47621
47622        let this = args
47623            .get(0)
47624            .cloned()
47625            .map(Box::new)
47626            .unwrap_or_else(|| Box::new(Expression::Null(Null)));
47627        let expression = args
47628            .get(1)
47629            .cloned()
47630            .map(Box::new)
47631            .unwrap_or_else(|| Box::new(Expression::Null(Null)));
47632        let count = args.get(2).cloned().map(Box::new);
47633
47634        if is_max {
47635            Ok(Some(Expression::ArgMax(Box::new(ArgMax {
47636                this,
47637                expression,
47638                count,
47639            }))))
47640        } else {
47641            Ok(Some(Expression::ArgMin(Box::new(ArgMin {
47642                this,
47643                expression,
47644                count,
47645            }))))
47646        }
47647    }
47648
47649    /// Parse MERGE statement
47650    /// Python: def _parse_merge(self) -> exp.Merge
47651    pub fn parse_merge(&mut self) -> Result<Option<Expression>> {
47652        // Optional INTO keyword
47653        self.match_token(TokenType::Into);
47654
47655        // Parse target table using parse_table_ref
47656        let mut target = Expression::Table(Box::new(self.parse_table_ref()?));
47657
47658        // Parse optional TSQL table hints: WITH (HOLDLOCK), WITH (TABLOCK), etc.
47659        if self.check(TokenType::With) && self.check_next(TokenType::LParen) {
47660            if let Expression::Table(ref mut table) = target {
47661                if let Some(hint_expr) = self.parse_table_hints()? {
47662                    match hint_expr {
47663                        Expression::Tuple(tuple) => {
47664                            table.hints = tuple.expressions;
47665                        }
47666                        other => {
47667                            table.hints = vec![other];
47668                        }
47669                    }
47670                }
47671            }
47672        }
47673
47674        // Parse optional alias for target table
47675        // Try to get an identifier as alias if AS is present or there's an identifier
47676        // Use parse_id_var instead of parse_identifier to handle Var tokens (e.g. T)
47677        if self.match_token(TokenType::As) {
47678            if let Some(alias_expr) = self.parse_id_var()? {
47679                // Extract identifier from the expression
47680                if let Expression::Identifier(ident) = alias_expr {
47681                    target = Expression::Alias(Box::new(Alias {
47682                        this: target,
47683                        alias: ident,
47684                        column_aliases: Vec::new(),
47685                        pre_alias_comments: Vec::new(),
47686                        trailing_comments: Vec::new(),
47687                        inferred_type: None,
47688                    }));
47689                }
47690            }
47691        } else if !self.check(TokenType::Using) {
47692            // Try to parse alias without AS keyword (e.g., MERGE t1 T USING ...)
47693            // Use parse_id_var to handle both Identifier and Var tokens
47694            if let Some(alias_expr) = self.parse_id_var()? {
47695                if let Expression::Identifier(ident) = alias_expr {
47696                    target = Expression::Alias(Box::new(Alias {
47697                        this: target,
47698                        alias: ident,
47699                        column_aliases: Vec::new(),
47700                        pre_alias_comments: Vec::new(),
47701                        trailing_comments: Vec::new(),
47702                        inferred_type: None,
47703                    }));
47704                }
47705            }
47706        }
47707
47708        // USING clause
47709        if !self.match_token(TokenType::Using) {
47710            return Err(self.parse_error("Expected USING in MERGE statement"));
47711        }
47712
47713        // Parse source table or subquery
47714        let mut using = if self.match_token(TokenType::LParen) {
47715            // Subquery: USING (SELECT ...) AS alias
47716            let query = self.parse_statement()?;
47717            self.expect(TokenType::RParen)?;
47718            let trailing = self.previous_trailing_comments().to_vec();
47719            let mut subq = Subquery {
47720                this: query,
47721                alias: None,
47722                column_aliases: Vec::new(),
47723                order_by: None,
47724                limit: None,
47725                offset: None,
47726                distribute_by: None,
47727                sort_by: None,
47728                cluster_by: None,
47729                lateral: false,
47730                modifiers_inside: false,
47731                trailing_comments: trailing,
47732                inferred_type: None,
47733            };
47734            // Parse optional alias: (SELECT ...) AS y(col1, col2)
47735            if self.match_token(TokenType::As) {
47736                let alias_name = self.expect_identifier_or_keyword()?;
47737                subq.alias = Some(Identifier::new(alias_name));
47738                // Parse optional column aliases: AS alias(col1, col2)
47739                if self.match_token(TokenType::LParen) {
47740                    let mut cols = Vec::new();
47741                    loop {
47742                        let col_name = self.expect_identifier_or_keyword()?;
47743                        cols.push(Identifier::new(col_name));
47744                        if !self.match_token(TokenType::Comma) {
47745                            break;
47746                        }
47747                    }
47748                    self.expect(TokenType::RParen)?;
47749                    subq.column_aliases = cols;
47750                }
47751            } else if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
47752                // Implicit alias without AS
47753                let alias_name = self.expect_identifier_or_keyword()?;
47754                subq.alias = Some(Identifier::new(alias_name));
47755                // Parse optional column aliases: alias(col1, col2)
47756                if self.match_token(TokenType::LParen) {
47757                    let mut cols = Vec::new();
47758                    loop {
47759                        let col_name = self.expect_identifier_or_keyword()?;
47760                        cols.push(Identifier::new(col_name));
47761                        if !self.match_token(TokenType::Comma) {
47762                            break;
47763                        }
47764                    }
47765                    self.expect(TokenType::RParen)?;
47766                    subq.column_aliases = cols;
47767                }
47768            }
47769            Expression::Subquery(Box::new(subq))
47770        } else {
47771            Expression::Table(Box::new(self.parse_table_ref()?))
47772        };
47773
47774        // Parse optional alias for source (if not already parsed for subquery)
47775        if matches!(&using, Expression::Table(_)) {
47776            if self.match_token(TokenType::As) {
47777                if let Some(alias_expr) = self.parse_id_var()? {
47778                    if let Expression::Identifier(ident) = alias_expr {
47779                        using = Expression::Alias(Box::new(Alias {
47780                            this: using,
47781                            alias: ident,
47782                            column_aliases: Vec::new(),
47783                            pre_alias_comments: Vec::new(),
47784                            trailing_comments: Vec::new(),
47785                            inferred_type: None,
47786                        }));
47787                    }
47788                }
47789            } else if !self.check(TokenType::On) {
47790                // Try to parse alias without AS keyword
47791                // Use parse_id_var to handle both Identifier and Var tokens (e.g., S, T)
47792                if let Some(alias_expr) = self.parse_id_var()? {
47793                    if let Expression::Identifier(ident) = alias_expr {
47794                        using = Expression::Alias(Box::new(Alias {
47795                            this: using,
47796                            alias: ident,
47797                            column_aliases: Vec::new(),
47798                            pre_alias_comments: Vec::new(),
47799                            trailing_comments: Vec::new(),
47800                            inferred_type: None,
47801                        }));
47802                    }
47803                }
47804            }
47805        }
47806
47807        // ON clause with condition
47808        let on = if self.match_token(TokenType::On) {
47809            Some(Box::new(self.parse_expression()?))
47810        } else {
47811            None
47812        };
47813
47814        // Optional additional USING clause for key columns (DuckDB: USING (col1, col2))
47815        let using_cond = if self.match_token(TokenType::Using) {
47816            // Parse comma-separated identifiers wrapped in parentheses
47817            if self.match_token(TokenType::LParen) {
47818                let mut idents = Vec::new();
47819                loop {
47820                    // Use parse_id_var to handle Var tokens (unquoted identifiers)
47821                    if let Some(ident) = self.parse_id_var()? {
47822                        idents.push(ident);
47823                    } else {
47824                        break;
47825                    }
47826                    if !self.match_token(TokenType::Comma) {
47827                        break;
47828                    }
47829                }
47830                self.match_token(TokenType::RParen);
47831                if !idents.is_empty() {
47832                    Some(Box::new(Expression::Tuple(Box::new(Tuple {
47833                        expressions: idents,
47834                    }))))
47835                } else {
47836                    None
47837                }
47838            } else {
47839                // Also support without parentheses for backwards compatibility
47840                let mut idents = Vec::new();
47841                loop {
47842                    if let Some(ident) = self.parse_id_var()? {
47843                        idents.push(ident);
47844                    } else {
47845                        break;
47846                    }
47847                    if !self.match_token(TokenType::Comma) {
47848                        break;
47849                    }
47850                }
47851                if !idents.is_empty() {
47852                    Some(Box::new(Expression::Tuple(Box::new(Tuple {
47853                        expressions: idents,
47854                    }))))
47855                } else {
47856                    None
47857                }
47858            }
47859        } else {
47860            None
47861        };
47862
47863        // Parse WHEN MATCHED clauses
47864        let whens = self.parse_when_matched_clauses()?;
47865
47866        // Parse optional RETURNING clause (PostgreSQL) or OUTPUT clause (TSQL)
47867        let returning = if let Some(ret) = self.parse_returning()? {
47868            Some(ret)
47869        } else if self.match_token(TokenType::Output) {
47870            // TSQL OUTPUT clause: OUTPUT $action, Inserted.col, Deleted.col [INTO target]
47871            let output = self.parse_output_clause()?;
47872            Some(Expression::Returning(Box::new(Returning {
47873                expressions: output.columns,
47874                into: output.into_table.map(Box::new),
47875            })))
47876        } else {
47877            None
47878        };
47879
47880        Ok(Some(Expression::Merge(Box::new(Merge {
47881            this: Box::new(target),
47882            using: Box::new(using),
47883            on,
47884            using_cond,
47885            whens: whens.map(Box::new),
47886            with_: None,
47887            returning: returning.map(Box::new),
47888        }))))
47889    }
47890
47891    /// Parse multiple WHEN [NOT] MATCHED clauses for MERGE
47892    fn parse_when_matched_clauses(&mut self) -> Result<Option<Expression>> {
47893        let mut whens = Vec::new();
47894
47895        while self.match_token(TokenType::When) {
47896            // Check for NOT MATCHED
47897            let matched = !self.match_token(TokenType::Not);
47898            self.match_text_seq(&["MATCHED"]);
47899
47900            // Check for BY TARGET or BY SOURCE
47901            let source = if self.match_text_seq(&["BY", "TARGET"]) {
47902                Some(Box::new(Expression::Boolean(BooleanLiteral {
47903                    value: false,
47904                })))
47905            } else if self.match_text_seq(&["BY", "SOURCE"]) {
47906                Some(Box::new(Expression::Boolean(BooleanLiteral {
47907                    value: true,
47908                })))
47909            } else {
47910                None
47911            };
47912
47913            // Optional AND condition
47914            let condition = if self.match_token(TokenType::And) {
47915                Some(Box::new(self.parse_expression()?))
47916            } else {
47917                None
47918            };
47919
47920            // THEN action
47921            if !self.match_token(TokenType::Then) {
47922                return Err(self.parse_error("Expected THEN in WHEN clause"));
47923            }
47924
47925            // Parse the action: INSERT, UPDATE, DELETE, or other keywords (DO NOTHING, etc.)
47926            let then: Expression = if self.match_token(TokenType::Insert) {
47927                // INSERT action - use Tuple to represent it
47928                let mut elements = vec![Expression::Var(Box::new(Var {
47929                    this: "INSERT".to_string(),
47930                }))];
47931
47932                // Spark/Databricks: INSERT * (insert all columns)
47933                if self.match_token(TokenType::Star) {
47934                    elements.push(Expression::Star(crate::expressions::Star {
47935                        table: None,
47936                        except: None,
47937                        replace: None,
47938                        rename: None,
47939                        trailing_comments: Vec::new(),
47940                        span: None,
47941                    }));
47942                } else
47943                // Parse column list (optional)
47944                if self.match_token(TokenType::LParen) {
47945                    let mut columns: Vec<Expression> = Vec::new();
47946                    loop {
47947                        if let Some(col) = self.parse_id_var()? {
47948                            // Handle qualified column references (e.g., target.a)
47949                            let col = if self.match_token(TokenType::Dot) {
47950                                if let Expression::Identifier(table_ident) = col {
47951                                    if let Some(col_expr) = self.parse_id_var()? {
47952                                        if let Expression::Identifier(col_ident) = col_expr {
47953                                            Expression::boxed_column(Column {
47954                                                name: col_ident,
47955                                                table: Some(table_ident),
47956                                                join_mark: false,
47957                                                trailing_comments: Vec::new(),
47958                                                span: None,
47959                                                inferred_type: None,
47960                                            })
47961                                        } else {
47962                                            col_expr
47963                                        }
47964                                    } else {
47965                                        return Err(self.parse_error(
47966                                            "Expected column name after dot in MERGE INSERT",
47967                                        ));
47968                                    }
47969                                } else {
47970                                    col
47971                                }
47972                            } else {
47973                                col
47974                            };
47975                            columns.push(col);
47976                        } else {
47977                            break;
47978                        }
47979                        if !self.match_token(TokenType::Comma) {
47980                            break;
47981                        }
47982                    }
47983                    self.match_token(TokenType::RParen);
47984                    if !columns.is_empty() {
47985                        elements.push(Expression::Tuple(Box::new(Tuple {
47986                            expressions: columns,
47987                        })));
47988                    }
47989                }
47990
47991                // Parse VALUES clause
47992                if self.match_text_seq(&["VALUES"]) {
47993                    if let Some(values) = self.parse_value()? {
47994                        elements.push(values);
47995                    }
47996                } else if self.match_text_seq(&["ROW"]) {
47997                    elements.push(Expression::Var(Box::new(Var {
47998                        this: "ROW".to_string(),
47999                    })));
48000                }
48001
48002                if elements.len() == 1 {
48003                    elements[0].clone()
48004                } else {
48005                    Expression::Tuple(Box::new(Tuple {
48006                        expressions: elements,
48007                    }))
48008                }
48009            } else if self.match_token(TokenType::Update) {
48010                // UPDATE action - use Tuple to represent SET assignments
48011                let mut elements = vec![Expression::Var(Box::new(Var {
48012                    this: "UPDATE".to_string(),
48013                }))];
48014
48015                // Spark/Databricks: UPDATE * (update all columns)
48016                if self.match_token(TokenType::Star) {
48017                    elements.push(Expression::Star(crate::expressions::Star {
48018                        table: None,
48019                        except: None,
48020                        replace: None,
48021                        rename: None,
48022                        trailing_comments: Vec::new(),
48023                        span: None,
48024                    }));
48025                } else if self.match_token(TokenType::Set) {
48026                    // Parse col = value assignments manually
48027                    let mut assignments: Vec<Expression> = Vec::new();
48028                    loop {
48029                        // Parse: column = expression (column can be qualified like x.a)
48030                        if let Some(col) = self.parse_id_var()? {
48031                            // Handle qualified column references (e.g., x.a = y.b)
48032                            let col = if self.match_token(TokenType::Dot) {
48033                                // We have a qualified column reference
48034                                if let Expression::Identifier(table_ident) = col {
48035                                    // Parse the column part after the dot
48036                                    if let Some(col_expr) = self.parse_id_var()? {
48037                                        if let Expression::Identifier(col_ident) = col_expr {
48038                                            Expression::boxed_column(Column {
48039                                                name: col_ident,
48040                                                table: Some(table_ident),
48041                                                join_mark: false,
48042                                                trailing_comments: Vec::new(),
48043                                                span: None,
48044                                                inferred_type: None,
48045                                            })
48046                                        } else {
48047                                            col_expr
48048                                        }
48049                                    } else {
48050                                        return Err(
48051                                            self.parse_error("Expected column name after dot")
48052                                        );
48053                                    }
48054                                } else {
48055                                    col
48056                                }
48057                            } else {
48058                                col
48059                            };
48060                            if self.match_token(TokenType::Eq) {
48061                                let value = self.parse_expression()?;
48062                                // Create assignment as EQ expression
48063                                let assignment = Expression::Eq(Box::new(BinaryOp {
48064                                    left: col,
48065                                    right: value,
48066                                    left_comments: Vec::new(),
48067                                    operator_comments: Vec::new(),
48068                                    trailing_comments: Vec::new(),
48069                                    inferred_type: None,
48070                                }));
48071                                assignments.push(assignment);
48072                            }
48073                        }
48074                        if !self.match_token(TokenType::Comma) {
48075                            break;
48076                        }
48077                    }
48078                    if !assignments.is_empty() {
48079                        elements.push(Expression::Tuple(Box::new(Tuple {
48080                            expressions: assignments,
48081                        })));
48082                    }
48083                }
48084
48085                if elements.len() == 1 {
48086                    elements[0].clone()
48087                } else {
48088                    Expression::Tuple(Box::new(Tuple {
48089                        expressions: elements,
48090                    }))
48091                }
48092            } else if self.match_token(TokenType::Delete) {
48093                // DELETE action
48094                Expression::Var(Box::new(Var {
48095                    this: "DELETE".to_string(),
48096                }))
48097            } else if self.match_identifier("DO") {
48098                // DO NOTHING action (PostgreSQL)
48099                if self.match_identifier("NOTHING") {
48100                    Expression::Var(Box::new(Var {
48101                        this: "DO NOTHING".to_string(),
48102                    }))
48103                } else {
48104                    return Err(self.parse_error("Expected NOTHING after DO"));
48105                }
48106            } else {
48107                // Other action
48108                if let Some(var) = self.parse_var()? {
48109                    var
48110                } else {
48111                    return Err(
48112                        self.parse_error("Expected INSERT, UPDATE, DELETE, or action keyword")
48113                    );
48114                }
48115            };
48116
48117            whens.push(Expression::When(Box::new(When {
48118                matched: Some(Box::new(Expression::Boolean(BooleanLiteral {
48119                    value: matched,
48120                }))),
48121                source,
48122                condition,
48123                then: Box::new(then),
48124            })));
48125        }
48126
48127        if whens.is_empty() {
48128            Ok(None)
48129        } else {
48130            Ok(Some(Expression::Whens(Box::new(Whens {
48131                expressions: whens,
48132            }))))
48133        }
48134    }
48135
48136    /// parse_mergeblockratio - Parses MERGEBLOCKRATIO property (Teradata)
48137    /// Python: _parse_mergeblockratio
48138    /// Format: MERGEBLOCKRATIO = number [PERCENT] or NO MERGEBLOCKRATIO or DEFAULT MERGEBLOCKRATIO
48139    pub fn parse_mergeblockratio(&mut self) -> Result<Option<Expression>> {
48140        self.parse_mergeblockratio_impl(false, false)
48141    }
48142
48143    /// Implementation of parse_mergeblockratio with options
48144    pub fn parse_mergeblockratio_impl(
48145        &mut self,
48146        no: bool,
48147        default: bool,
48148    ) -> Result<Option<Expression>> {
48149        // Check for = followed by a number
48150        if self.match_token(TokenType::Eq) {
48151            let this = self.parse_number()?;
48152            let percent = self.match_token(TokenType::Percent);
48153
48154            Ok(Some(Expression::MergeBlockRatioProperty(Box::new(
48155                MergeBlockRatioProperty {
48156                    this: this.map(Box::new),
48157                    no: None,
48158                    default: None,
48159                    percent: if percent {
48160                        Some(Box::new(Expression::Boolean(BooleanLiteral {
48161                            value: true,
48162                        })))
48163                    } else {
48164                        None
48165                    },
48166                },
48167            ))))
48168        } else {
48169            // NO or DEFAULT variant
48170            Ok(Some(Expression::MergeBlockRatioProperty(Box::new(
48171                MergeBlockRatioProperty {
48172                    this: None,
48173                    no: if no {
48174                        Some(Box::new(Expression::Boolean(BooleanLiteral {
48175                            value: true,
48176                        })))
48177                    } else {
48178                        None
48179                    },
48180                    default: if default {
48181                        Some(Box::new(Expression::Boolean(BooleanLiteral {
48182                            value: true,
48183                        })))
48184                    } else {
48185                        None
48186                    },
48187                    percent: None,
48188                },
48189            ))))
48190        }
48191    }
48192
48193    /// parse_modifies_property - Implemented from Python _parse_modifies_property
48194    #[allow(unused_variables, unused_mut)]
48195    pub fn parse_modifies_property(&mut self) -> Result<Option<Expression>> {
48196        if self.match_text_seq(&["SQL", "DATA"]) {
48197            // Matched: SQL DATA
48198            return Ok(None);
48199        }
48200        Ok(None)
48201    }
48202
48203    /// parse_multitable_inserts - Parses Oracle's multi-table INSERT (INSERT ALL/FIRST)
48204    /// Python: _parse_multitable_inserts
48205    /// Syntax: INSERT ALL|FIRST [WHEN cond THEN] INTO table [(cols)] [VALUES(...)] ... SELECT ...
48206    pub fn parse_multitable_inserts(
48207        &mut self,
48208        leading_comments: Vec<String>,
48209    ) -> Result<Option<Expression>> {
48210        // Get kind from previous token (ALL or FIRST)
48211        let kind = self.previous().text.to_ascii_uppercase();
48212
48213        let mut expressions = Vec::new();
48214
48215        // Helper closure to parse a single conditional insert
48216        // Returns None when no more INTO clauses found
48217        loop {
48218            // Check for WHEN condition
48219            let condition = if self.match_token(TokenType::When) {
48220                let cond = self.parse_or()?;
48221                self.match_token(TokenType::Then);
48222                Some(cond)
48223            } else {
48224                None
48225            };
48226
48227            // Check for ELSE (used in INSERT FIRST ... ELSE INTO ...)
48228            let is_else = self.match_token(TokenType::Else);
48229
48230            // Must have INTO keyword to continue
48231            if !self.match_token(TokenType::Into) {
48232                break;
48233            }
48234
48235            // Parse table with optional schema (using parse_table_parts for proper schema.table parsing)
48236            let table_expr = self.parse_table_parts()?;
48237
48238            // Extract TableRef from the table expression
48239            let table_ref = if let Some(Expression::Table(t)) = table_expr {
48240                *t
48241            } else {
48242                // Fallback: create empty table ref (shouldn't happen)
48243                TableRef::new("")
48244            };
48245
48246            // Parse optional column list: (col1, col2, ...)
48247            let columns = if self.match_token(TokenType::LParen) {
48248                let cols = self.parse_identifier_list()?;
48249                self.expect(TokenType::RParen)?;
48250                cols
48251            } else {
48252                Vec::new()
48253            };
48254
48255            // Parse optional VALUES clause
48256            let values = if self.match_token(TokenType::Values) {
48257                self.expect(TokenType::LParen)?;
48258                let row = self.parse_expression_list()?;
48259                self.expect(TokenType::RParen)?;
48260                vec![row]
48261            } else {
48262                Vec::new()
48263            };
48264
48265            // Create Insert expression for this INTO clause
48266            let insert_expr = Expression::Insert(Box::new(Insert {
48267                table: table_ref,
48268                columns,
48269                values,
48270                query: None,
48271                overwrite: false,
48272                partition: Vec::new(),
48273                directory: None,
48274                returning: Vec::new(),
48275                output: None,
48276                on_conflict: None,
48277                leading_comments: Vec::new(),
48278                if_exists: false,
48279                with: None,
48280                ignore: false,
48281                source_alias: None,
48282                alias: None,
48283                alias_explicit_as: false,
48284                default_values: false,
48285                by_name: false,
48286                conflict_action: None,
48287                is_replace: false,
48288                replace_where: None,
48289                source: None,
48290                hint: None,
48291                function_target: None,
48292                partition_by: None,
48293                settings: Vec::new(),
48294            }));
48295
48296            // Wrap in ConditionalInsert
48297            let conditional_insert = Expression::ConditionalInsert(Box::new(ConditionalInsert {
48298                this: Box::new(insert_expr),
48299                expression: condition.map(Box::new),
48300                else_: if is_else {
48301                    Some(Box::new(Expression::Boolean(BooleanLiteral {
48302                        value: true,
48303                    })))
48304                } else {
48305                    None
48306                },
48307            }));
48308
48309            expressions.push(conditional_insert);
48310        }
48311
48312        // Parse the source SELECT statement (or subquery)
48313        let source = self.parse_statement()?;
48314
48315        Ok(Some(Expression::MultitableInserts(Box::new(
48316            MultitableInserts {
48317                kind,
48318                expressions,
48319                source: Some(Box::new(source)),
48320                leading_comments,
48321            },
48322        ))))
48323    }
48324
48325    /// parse_name_as_expression - Parse identifier that can be aliased
48326    /// Parses: identifier [AS expression]
48327    #[allow(unused_variables, unused_mut)]
48328    pub fn parse_name_as_expression(&mut self) -> Result<Option<Expression>> {
48329        // Parse the identifier
48330        let this = self.parse_id_var()?;
48331        if this.is_none() {
48332            return Ok(None);
48333        }
48334
48335        // Check for AS alias
48336        if self.match_token(TokenType::Alias) {
48337            let expression = self.parse_disjunction()?;
48338            if expression.is_none() {
48339                return Ok(this);
48340            }
48341
48342            // Extract the identifier for the alias
48343            let alias_ident =
48344                match this.ok_or_else(|| self.parse_error("Expected identifier for alias"))? {
48345                    Expression::Identifier(id) => id,
48346                    _ => Identifier::new(String::new()),
48347                };
48348
48349            return Ok(Some(Expression::Alias(Box::new(Alias {
48350                this: expression.ok_or_else(|| self.parse_error("Expected expression after AS"))?,
48351                alias: alias_ident,
48352                column_aliases: Vec::new(),
48353                pre_alias_comments: Vec::new(),
48354                trailing_comments: Vec::new(),
48355                inferred_type: None,
48356            }))));
48357        }
48358
48359        Ok(this)
48360    }
48361
48362    /// parse_named_window - Ported from Python _parse_named_window
48363    /// Parses a named window definition: name AS (spec)
48364    #[allow(unused_variables, unused_mut)]
48365    pub fn parse_named_window(&mut self) -> Result<Option<Expression>> {
48366        // Parse window name
48367        let name = self.parse_id_var()?;
48368        if name.is_none() {
48369            return Ok(None);
48370        }
48371
48372        // Expect AS
48373        if !self.match_token(TokenType::As) {
48374            return Ok(name); // Just the name, no spec
48375        }
48376
48377        // Parse window spec (parenthesized)
48378        self.expect(TokenType::LParen)?;
48379        let spec = self.parse_window_spec_inner()?;
48380        self.expect(TokenType::RParen)?;
48381
48382        if let (Some(name_expr), Some(spec_expr)) = (name, spec) {
48383            // Create an Alias expression wrapping the spec with the name
48384            let alias_ident = if let Expression::Identifier(id) = name_expr {
48385                id
48386            } else {
48387                Identifier::new("window")
48388            };
48389            Ok(Some(Expression::Alias(Box::new(Alias {
48390                this: spec_expr,
48391                alias: alias_ident,
48392                column_aliases: Vec::new(),
48393                pre_alias_comments: Vec::new(),
48394                trailing_comments: Vec::new(),
48395                inferred_type: None,
48396            }))))
48397        } else {
48398            Ok(None)
48399        }
48400    }
48401
48402    /// parse_next_value_for - Parses NEXT VALUE FOR sequence_name
48403    /// Python: parser.py:6752-6761
48404    #[allow(unused_variables, unused_mut)]
48405    pub fn parse_next_value_for(&mut self) -> Result<Option<Expression>> {
48406        if !self.match_text_seq(&["VALUE", "FOR"]) {
48407            // Retreat if we consumed a token
48408            if self.current > 0 {
48409                self.current -= 1;
48410            }
48411            return Ok(None);
48412        }
48413
48414        // Parse the sequence name as a dotted identifier (db.schema.sequence_name)
48415        // Manually parse identifier parts separated by dots
48416        let first = self
48417            .parse_id_var()?
48418            .ok_or_else(|| self.parse_error("Expected sequence name after NEXT VALUE FOR"))?;
48419        let first_id = match first {
48420            Expression::Identifier(id) => id,
48421            Expression::Var(v) => Identifier {
48422                name: v.this,
48423                quoted: false,
48424                trailing_comments: Vec::new(),
48425                span: None,
48426            },
48427            _ => Identifier {
48428                name: String::new(),
48429                quoted: false,
48430                trailing_comments: Vec::new(),
48431                span: None,
48432            },
48433        };
48434
48435        // Check for dotted parts (db.schema.sequence_name)
48436        let mut parts = vec![first_id];
48437        while self.match_token(TokenType::Dot) {
48438            if self.is_identifier_or_keyword_token() {
48439                let token = self.advance();
48440                parts.push(Identifier {
48441                    name: token.text,
48442                    quoted: token.token_type == TokenType::QuotedIdentifier,
48443                    trailing_comments: Vec::new(),
48444                    span: None,
48445                });
48446            } else {
48447                break;
48448            }
48449        }
48450
48451        // Build a Column expression from the parts
48452        let this = if parts.len() == 1 {
48453            Expression::boxed_column(Column {
48454                name: parts.remove(0),
48455                table: None,
48456                join_mark: false,
48457                trailing_comments: Vec::new(),
48458                span: None,
48459                inferred_type: None,
48460            })
48461        } else if parts.len() == 2 {
48462            Expression::boxed_column(Column {
48463                name: parts.remove(1),
48464                table: Some(parts.remove(0)),
48465                join_mark: false,
48466                trailing_comments: Vec::new(),
48467                span: None,
48468                inferred_type: None,
48469            })
48470        } else {
48471            // For 3+ parts, build nested Dot expressions
48472            let mut expr = Expression::Identifier(parts.remove(0));
48473            for part in parts.drain(..) {
48474                expr = Expression::Dot(Box::new(DotAccess {
48475                    this: expr,
48476                    field: part,
48477                }));
48478            }
48479            expr
48480        };
48481
48482        // Parse optional OVER (ORDER BY ...) clause
48483        let order = if self.match_token(TokenType::Over) {
48484            if self.match_token(TokenType::LParen) {
48485                let ord = self.parse_order()?;
48486                self.expect(TokenType::RParen)?;
48487                ord.map(Box::new)
48488            } else {
48489                None
48490            }
48491        } else {
48492            None
48493        };
48494
48495        Ok(Some(Expression::NextValueFor(Box::new(NextValueFor {
48496            this: Box::new(this),
48497            order,
48498        }))))
48499    }
48500
48501    /// parse_no_property - Implemented from Python _parse_no_property
48502    #[allow(unused_variables, unused_mut)]
48503    pub fn parse_no_property(&mut self) -> Result<Option<Expression>> {
48504        if self.match_text_seq(&["PRIMARY", "INDEX"]) {
48505            // Matched: PRIMARY INDEX
48506            return Ok(None);
48507        }
48508        if self.match_text_seq(&["SQL"]) {
48509            // Matched: SQL
48510            return Ok(None);
48511        }
48512        Ok(None)
48513    }
48514
48515    /// parse_normalize - Ported from Python _parse_normalize
48516    #[allow(unused_variables, unused_mut)]
48517    /// parse_normalize - Parses NORMALIZE(expr [, form])
48518    /// Python: NORMALIZE(expr, form) where form is NFC/NFD/NFKC/NFKD
48519    pub fn parse_normalize(&mut self) -> Result<Option<Expression>> {
48520        // Parse the expression to normalize
48521        let this = self.parse_expression()?;
48522
48523        // Check for optional form argument
48524        let form = if self.match_token(TokenType::Comma) {
48525            self.parse_var()?.map(Box::new)
48526        } else {
48527            None
48528        };
48529
48530        Ok(Some(Expression::Normalize(Box::new(Normalize {
48531            this: Box::new(this),
48532            form,
48533            is_casefold: None,
48534        }))))
48535    }
48536
48537    /// parse_not_constraint - Implemented from Python _parse_not_constraint
48538    /// Parses constraints that start with NOT: NOT NULL, NOT CASESPECIFIC
48539    pub fn parse_not_constraint(&mut self) -> Result<Option<Expression>> {
48540        // NOT NULL constraint
48541        if self.match_text_seq(&["NULL"]) {
48542            return Ok(Some(Expression::NotNullColumnConstraint(Box::new(
48543                NotNullColumnConstraint { allow_null: None },
48544            ))));
48545        }
48546        // NOT CASESPECIFIC constraint (Teradata)
48547        if self.match_text_seq(&["CASESPECIFIC"]) {
48548            return Ok(Some(Expression::CaseSpecificColumnConstraint(Box::new(
48549                CaseSpecificColumnConstraint {
48550                    not_: Some(Box::new(Expression::Boolean(BooleanLiteral {
48551                        value: true,
48552                    }))),
48553                },
48554            ))));
48555        }
48556        // NOT FOR REPLICATION (SQL Server) - consume the tokens and return as a property
48557        if self.match_token(TokenType::For) && self.match_identifier("REPLICATION") {
48558            return Ok(Some(Expression::Property(Box::new(
48559                crate::expressions::Property {
48560                    this: Box::new(Expression::Identifier(Identifier::new(
48561                        "NOT FOR REPLICATION".to_string(),
48562                    ))),
48563                    value: None,
48564                },
48565            ))));
48566        }
48567        Ok(None)
48568    }
48569
48570    /// parse_null - Parse NULL literal
48571    /// Python: if self._match_set((TokenType.NULL, TokenType.UNKNOWN)): return exp.Null
48572    pub fn parse_null(&mut self) -> Result<Option<Expression>> {
48573        if self.match_token(TokenType::Null) {
48574            return Ok(Some(Expression::Null(Null)));
48575        }
48576        // UNKNOWN is treated as NULL in some dialects
48577        if self.match_token(TokenType::Unknown) {
48578            return Ok(Some(Expression::Null(Null)));
48579        }
48580        Ok(None)
48581    }
48582
48583    /// parse_number - Parse numeric literal
48584    /// Python: TokenType.NUMBER -> exp.Literal(this=token.text, is_string=False)
48585    /// Handles Hive/Spark numeric suffixes encoded as "number::TYPE" by the tokenizer
48586    pub fn parse_number(&mut self) -> Result<Option<Expression>> {
48587        if self.match_token(TokenType::Number) {
48588            let text = self.previous().text.clone();
48589            // Check for numeric literal suffix encoded as "number::TYPE"
48590            if let Some(sep_pos) = text.find("::") {
48591                let num_part = &text[..sep_pos];
48592                let type_name = &text[sep_pos + 2..];
48593                // Create a TryCast expression: TRY_CAST(number AS TYPE)
48594                let num_expr = Expression::Literal(Literal::Number(num_part.to_string()));
48595                let data_type = match type_name {
48596                    "BIGINT" => crate::expressions::DataType::BigInt { length: None },
48597                    "SMALLINT" => crate::expressions::DataType::SmallInt { length: None },
48598                    "TINYINT" => crate::expressions::DataType::TinyInt { length: None },
48599                    "DOUBLE" => crate::expressions::DataType::Double {
48600                        precision: None,
48601                        scale: None,
48602                    },
48603                    "FLOAT" => crate::expressions::DataType::Float {
48604                        precision: None,
48605                        scale: None,
48606                        real_spelling: false,
48607                    },
48608                    "DECIMAL" => crate::expressions::DataType::Decimal {
48609                        precision: None,
48610                        scale: None,
48611                    },
48612                    _ => crate::expressions::DataType::Custom {
48613                        name: type_name.to_string(),
48614                    },
48615                };
48616                return Ok(Some(Expression::TryCast(Box::new(
48617                    crate::expressions::Cast {
48618                        this: num_expr,
48619                        to: data_type,
48620                        trailing_comments: Vec::new(),
48621                        double_colon_syntax: false,
48622                        format: None,
48623                        default: None,
48624                        inferred_type: None,
48625                    },
48626                ))));
48627            }
48628            return Ok(Some(Expression::Literal(Literal::Number(text))));
48629        }
48630        Ok(None)
48631    }
48632
48633    /// parse_odbc_datetime_literal - Ported from Python _parse_odbc_datetime_literal
48634    #[allow(unused_variables, unused_mut)]
48635    /// parse_odbc_datetime_literal - Parses ODBC datetime literals
48636    /// Examples: {d'2023-01-01'}, {t'12:00:00'}, {ts'2023-01-01 12:00:00'}
48637    pub fn parse_odbc_datetime_literal(&mut self) -> Result<Option<Expression>> {
48638        // Match the type indicator (d, t, ts)
48639        if !self.match_token(TokenType::Var) {
48640            return Ok(None);
48641        }
48642        let type_indicator = self.previous().text.to_lowercase();
48643
48644        // Parse the string value
48645        let value = self.parse_string()?;
48646        if value.is_none() {
48647            return Ok(None);
48648        }
48649
48650        // Expect closing brace
48651        self.expect(TokenType::RBrace)?;
48652
48653        // Return appropriate expression based on type
48654        let value = value
48655            .ok_or_else(|| self.parse_error("Expected string value in ODBC datetime literal"))?;
48656        match type_indicator.as_str() {
48657            "d" => Ok(Some(Expression::Date(Box::new(UnaryFunc::new(value))))),
48658            "t" => Ok(Some(Expression::Time(Box::new(UnaryFunc::new(value))))),
48659            "ts" => Ok(Some(Expression::Timestamp(Box::new(TimestampFunc {
48660                this: Some(Box::new(value)),
48661                zone: None,
48662                with_tz: None,
48663                safe: None,
48664            })))),
48665            _ => Ok(Some(value)),
48666        }
48667    }
48668
48669    /// parse_offset - Parse OFFSET clause
48670    /// Python: if self._match(TokenType.OFFSET): return exp.Offset(this=self._parse_term())
48671    pub fn parse_offset(&mut self) -> Result<Option<Expression>> {
48672        if !self.match_token(TokenType::Offset) {
48673            return Ok(None);
48674        }
48675        // Parse the offset expression (usually a number)
48676        let offset_expr = self.parse_expression()?;
48677        Ok(Some(Expression::Offset(Box::new(Offset {
48678            this: offset_expr,
48679            rows: None,
48680        }))))
48681    }
48682
48683    /// parse_on_condition - Ported from Python _parse_on_condition
48684    #[allow(unused_variables, unused_mut)]
48685    /// parse_on_condition - Parses ON EMPTY/ERROR/NULL conditions
48686    /// Example: NULL ON EMPTY, ERROR ON ERROR
48687    pub fn parse_on_condition(&mut self) -> Result<Option<Expression>> {
48688        // Parse ON EMPTY
48689        let empty = if self.match_text_seq(&["NULL", "ON", "EMPTY"]) {
48690            Some(Box::new(Expression::Identifier(Identifier::new(
48691                "NULL".to_string(),
48692            ))))
48693        } else if self.match_text_seq(&["ERROR", "ON", "EMPTY"]) {
48694            Some(Box::new(Expression::Identifier(Identifier::new(
48695                "ERROR".to_string(),
48696            ))))
48697        } else if self.match_text_seq(&["DEFAULT"]) {
48698            let default_val = self.parse_expression()?;
48699            if self.match_text_seq(&["ON", "EMPTY"]) {
48700                Some(Box::new(default_val))
48701            } else {
48702                None
48703            }
48704        } else {
48705            None
48706        };
48707
48708        // Parse ON ERROR
48709        let error = if self.match_text_seq(&["NULL", "ON", "ERROR"]) {
48710            Some(Box::new(Expression::Identifier(Identifier::new(
48711                "NULL".to_string(),
48712            ))))
48713        } else if self.match_text_seq(&["ERROR", "ON", "ERROR"]) {
48714            Some(Box::new(Expression::Identifier(Identifier::new(
48715                "ERROR".to_string(),
48716            ))))
48717        } else if self.match_text_seq(&["DEFAULT"]) {
48718            let default_val = self.parse_expression()?;
48719            if self.match_text_seq(&["ON", "ERROR"]) {
48720                Some(Box::new(default_val))
48721            } else {
48722                None
48723            }
48724        } else {
48725            None
48726        };
48727
48728        // Parse ON NULL
48729        let null = if self.match_text_seq(&["NULL", "ON", "NULL"]) {
48730            Some(Box::new(Expression::Identifier(Identifier::new(
48731                "NULL".to_string(),
48732            ))))
48733        } else {
48734            None
48735        };
48736
48737        if empty.is_none() && error.is_none() && null.is_none() {
48738            return Ok(None);
48739        }
48740
48741        Ok(Some(Expression::OnCondition(Box::new(OnCondition {
48742            empty,
48743            error,
48744            null,
48745        }))))
48746    }
48747
48748    /// parse_on_handling - Implemented from Python _parse_on_handling
48749    /// Calls: parse_bitwise
48750    #[allow(unused_variables, unused_mut)]
48751    pub fn parse_on_handling(&mut self) -> Result<Option<Expression>> {
48752        if self.match_text_seq(&["ON"]) {
48753            // Matched: ON
48754            return Ok(None);
48755        }
48756        if self.match_text_seq(&["ON"]) {
48757            // Matched: ON
48758            return Ok(None);
48759        }
48760        Ok(None)
48761    }
48762
48763    /// parse_on_property - Implemented from Python _parse_on_property
48764    #[allow(unused_variables, unused_mut)]
48765    pub fn parse_on_property(&mut self) -> Result<Option<Expression>> {
48766        if self.match_text_seq(&["COMMIT", "PRESERVE", "ROWS"]) {
48767            return Ok(Some(Expression::OnCommitProperty(Box::new(
48768                OnCommitProperty { delete: None },
48769            ))));
48770        }
48771        if self.match_text_seq(&["COMMIT", "DELETE", "ROWS"]) {
48772            // Matched: COMMIT DELETE ROWS
48773            return Ok(None);
48774        }
48775        Ok(None)
48776    }
48777
48778    /// parse_opclass - Ported from Python _parse_opclass
48779    #[allow(unused_variables, unused_mut)]
48780    /// parse_opclass - Parses PostgreSQL operator class in index expressions
48781    /// Example: column_name text_pattern_ops
48782    pub fn parse_opclass(&mut self) -> Result<Option<Expression>> {
48783        // Parse the expression first
48784        let this = self.parse_expression()?;
48785
48786        // Check for keywords that would indicate this is not an opclass
48787        // (e.g., ASC, DESC, NULLS, etc.)
48788        if self.check(TokenType::Asc)
48789            || self.check(TokenType::Desc)
48790            || self.check(TokenType::Nulls)
48791            || self.check(TokenType::Comma)
48792            || self.check(TokenType::RParen)
48793        {
48794            return Ok(Some(this));
48795        }
48796
48797        // Try to parse an operator class name (table parts)
48798        if let Some(opclass_name) = self.parse_table()? {
48799            return Ok(Some(Expression::Opclass(Box::new(Opclass {
48800                this: Box::new(this),
48801                expression: Box::new(opclass_name),
48802            }))));
48803        }
48804
48805        Ok(Some(this))
48806    }
48807
48808    /// parse_open_json - Parses SQL Server OPENJSON function
48809    /// Example: OPENJSON(json, '$.path') WITH (col1 type '$.path' AS JSON, ...)
48810    pub fn parse_open_json(&mut self) -> Result<Option<Expression>> {
48811        // Parse the JSON expression
48812        let this = self.parse_expression()?;
48813
48814        // Parse optional path
48815        let path = if self.match_token(TokenType::Comma) {
48816            self.parse_string()?.map(Box::new)
48817        } else {
48818            None
48819        };
48820
48821        // Check for closing paren and WITH clause
48822        let expressions = if self.match_token(TokenType::RParen)
48823            && self.match_token(TokenType::With)
48824        {
48825            self.expect(TokenType::LParen)?;
48826            let mut cols = Vec::new();
48827            loop {
48828                // Parse column definition: name type 'path' [AS JSON]
48829                let col_name = self.parse_field()?;
48830                if col_name.is_none() {
48831                    break;
48832                }
48833                let col_type = self.parse_data_type()?;
48834                let col_path = self.parse_string()?.map(Box::new);
48835                let as_json = if self.match_token(TokenType::As) && self.match_identifier("JSON") {
48836                    Some(Box::new(Expression::Boolean(BooleanLiteral {
48837                        value: true,
48838                    })))
48839                } else {
48840                    None
48841                };
48842                cols.push(Expression::OpenJSONColumnDef(Box::new(OpenJSONColumnDef {
48843                    this: Box::new(col_name.ok_or_else(|| {
48844                        self.parse_error("Expected column name in OPENJSON WITH clause")
48845                    })?),
48846                    kind: String::new(), // kept for backwards compat, use data_type instead
48847                    path: col_path,
48848                    as_json,
48849                    data_type: Some(col_type),
48850                })));
48851                if !self.match_token(TokenType::Comma) {
48852                    break;
48853                }
48854            }
48855            self.expect(TokenType::RParen)?;
48856            cols
48857        } else {
48858            Vec::new()
48859        };
48860
48861        Ok(Some(Expression::OpenJSON(Box::new(OpenJSON {
48862            this: Box::new(this),
48863            path,
48864            expressions,
48865        }))))
48866    }
48867
48868    /// parse_operator - Ported from Python _parse_operator
48869    #[allow(unused_variables, unused_mut)]
48870    /// parse_operator - Parses PostgreSQL OPERATOR(op) syntax
48871    /// Example: col1 OPERATOR(~>) col2
48872    pub fn parse_operator(&mut self, this: Option<Expression>) -> Result<Option<Expression>> {
48873        let mut result = this;
48874
48875        // Parse OPERATOR(op) expressions
48876        while self.match_token(TokenType::LParen) {
48877            // Collect the operator text between parens
48878            let mut op_text = String::new();
48879            while !self.check(TokenType::RParen) && !self.is_at_end() {
48880                op_text.push_str(&self.peek().text);
48881                self.skip();
48882            }
48883            self.expect(TokenType::RParen)?;
48884
48885            // Parse the right-hand side expression
48886            let rhs = self.parse_expression()?;
48887
48888            result = Some(Expression::Operator(Box::new(Operator {
48889                this: Box::new(result.unwrap_or_else(|| Expression::Null(Null))),
48890                operator: Some(Box::new(Expression::Identifier(Identifier::new(op_text)))),
48891                expression: Box::new(rhs),
48892                comments: Vec::new(),
48893            })));
48894
48895            // Check if there's another OPERATOR keyword
48896            if !self.match_token(TokenType::Operator) {
48897                break;
48898            }
48899        }
48900
48901        Ok(result)
48902    }
48903
48904    /// parse_order - Parse ORDER BY clause
48905    /// Python: if not self._match(TokenType.ORDER_BY): return this; return exp.Order(expressions=self._parse_csv(self._parse_ordered))
48906    pub fn parse_order(&mut self) -> Result<Option<Expression>> {
48907        if !self.match_token(TokenType::Order) {
48908            return Ok(None);
48909        }
48910        // Consume BY if present
48911        self.match_token(TokenType::By);
48912
48913        // Parse comma-separated ordered expressions
48914        let mut expressions = Vec::new();
48915        loop {
48916            if let Some(ordered) = self.parse_ordered_item()? {
48917                expressions.push(ordered);
48918            } else {
48919                break;
48920            }
48921            if !self.match_token(TokenType::Comma) {
48922                break;
48923            }
48924        }
48925
48926        Ok(Some(Expression::OrderBy(Box::new(OrderBy {
48927            expressions,
48928            siblings: false,
48929            comments: Vec::new(),
48930        }))))
48931    }
48932
48933    /// parse_ordered_item - Parse a single ORDER BY item (expr [ASC|DESC] [NULLS FIRST|LAST])
48934    fn parse_ordered_item(&mut self) -> Result<Option<Ordered>> {
48935        // Parse the expression to order by
48936        let expr = match self.parse_expression() {
48937            Ok(e) => e,
48938            Err(_) => return Ok(None),
48939        };
48940
48941        // Check for ASC/DESC
48942        let mut desc = false;
48943        let mut explicit_asc = false;
48944        if self.match_token(TokenType::Asc) {
48945            explicit_asc = true;
48946        } else if self.match_token(TokenType::Desc) {
48947            desc = true;
48948        }
48949
48950        // Check for NULLS FIRST/LAST
48951        let nulls_first = if self.match_text_seq(&["NULLS", "FIRST"]) {
48952            Some(true)
48953        } else if self.match_text_seq(&["NULLS", "LAST"]) {
48954            Some(false)
48955        } else {
48956            None
48957        };
48958
48959        // Parse optional WITH FILL clause (ClickHouse)
48960        let with_fill = if self.match_text_seq(&["WITH", "FILL"]) {
48961            let from_ = if self.match_token(TokenType::From) {
48962                Some(Box::new(self.parse_or()?))
48963            } else {
48964                None
48965            };
48966            let to = if self.match_text_seq(&["TO"]) {
48967                Some(Box::new(self.parse_or()?))
48968            } else {
48969                None
48970            };
48971            let step = if self.match_text_seq(&["STEP"]) {
48972                Some(Box::new(self.parse_or()?))
48973            } else {
48974                None
48975            };
48976            let staleness = if self.match_text_seq(&["STALENESS"]) {
48977                Some(Box::new(self.parse_or()?))
48978            } else {
48979                None
48980            };
48981            let interpolate = if self.match_text_seq(&["INTERPOLATE"]) {
48982                if self.match_token(TokenType::LParen) {
48983                    let exprs = self.parse_expression_list()?;
48984                    self.expect(TokenType::RParen)?;
48985                    if exprs.len() == 1 {
48986                        Some(Box::new(exprs.into_iter().next().unwrap()))
48987                    } else {
48988                        Some(Box::new(Expression::Tuple(Box::new(
48989                            crate::expressions::Tuple { expressions: exprs },
48990                        ))))
48991                    }
48992                } else {
48993                    None
48994                }
48995            } else {
48996                None
48997            };
48998            Some(Box::new(WithFill {
48999                from_,
49000                to,
49001                step,
49002                staleness,
49003                interpolate,
49004            }))
49005        } else {
49006            None
49007        };
49008
49009        Ok(Some(Ordered {
49010            this: expr,
49011            desc,
49012            nulls_first,
49013            explicit_asc,
49014            with_fill,
49015        }))
49016    }
49017
49018    /// parse_ordered - Implemented from Python _parse_ordered (wrapper for parse_ordered_item)
49019    #[allow(unused_variables, unused_mut)]
49020    pub fn parse_ordered(&mut self) -> Result<Option<Expression>> {
49021        if let Some(ordered) = self.parse_ordered_item()? {
49022            return Ok(Some(Expression::Ordered(Box::new(ordered))));
49023        }
49024        if self.match_text_seq(&["NULLS", "FIRST"]) {
49025            return Ok(Some(Expression::WithFill(Box::new(WithFill {
49026                from_: None,
49027                to: None,
49028                step: None,
49029                staleness: None,
49030                interpolate: None,
49031            }))));
49032        }
49033        if self.match_text_seq(&["NULLS", "LAST"]) {
49034            // Matched: NULLS LAST
49035            return Ok(None);
49036        }
49037        if self.match_text_seq(&["WITH", "FILL"]) {
49038            // Matched: WITH FILL
49039            return Ok(None);
49040        }
49041        Ok(None)
49042    }
49043
49044    /// parse_overlay - Ported from Python _parse_overlay
49045    /// Parses OVERLAY function: OVERLAY(string PLACING replacement FROM position [FOR length])
49046    #[allow(unused_variables, unused_mut)]
49047    pub fn parse_overlay(&mut self) -> Result<Option<Expression>> {
49048        // Parse the string to be modified
49049        let this = match self.parse_bitwise() {
49050            Ok(Some(expr)) => expr,
49051            Ok(None) => return Ok(None),
49052            Err(e) => return Err(e),
49053        };
49054
49055        // Parse PLACING replacement (or comma then replacement)
49056        let replacement = if self.match_text_seq(&["PLACING"]) || self.match_token(TokenType::Comma)
49057        {
49058            match self.parse_bitwise() {
49059                Ok(Some(expr)) => expr,
49060                Ok(None) => {
49061                    return Err(self.parse_error("Expected replacement expression in OVERLAY"))
49062                }
49063                Err(e) => return Err(e),
49064            }
49065        } else {
49066            return Err(self.parse_error("Expected PLACING in OVERLAY function"));
49067        };
49068
49069        // Parse FROM position (or comma then position)
49070        let from = if self.match_token(TokenType::From) || self.match_token(TokenType::Comma) {
49071            match self.parse_bitwise() {
49072                Ok(Some(expr)) => expr,
49073                Ok(None) => return Err(self.parse_error("Expected position expression in OVERLAY")),
49074                Err(e) => return Err(e),
49075            }
49076        } else {
49077            return Err(self.parse_error("Expected FROM in OVERLAY function"));
49078        };
49079
49080        // Parse optional FOR length (or comma then length)
49081        let length = if self.match_token(TokenType::For) || self.match_token(TokenType::Comma) {
49082            match self.parse_bitwise() {
49083                Ok(Some(expr)) => Some(expr),
49084                Ok(None) => None,
49085                Err(_) => None,
49086            }
49087        } else {
49088            None
49089        };
49090
49091        Ok(Some(Expression::Overlay(Box::new(OverlayFunc {
49092            this,
49093            replacement,
49094            from,
49095            length,
49096        }))))
49097    }
49098
49099    /// parse_parameter - Parse named parameter (@name or :name)
49100    /// Python: this = self._parse_identifier() or self._parse_primary_or_var(); return exp.Parameter(this=this)
49101    pub fn parse_parameter(&mut self) -> Result<Option<Expression>> {
49102        // Check for parameter token types
49103        if self.match_token(TokenType::Parameter) {
49104            let text = self.previous().text.clone();
49105            return Ok(Some(Expression::Parameter(Box::new(Parameter {
49106                name: Some(text),
49107                index: None,
49108                style: ParameterStyle::Colon,
49109                quoted: false,
49110                string_quoted: false,
49111                expression: None,
49112            }))));
49113        }
49114
49115        // Check for session parameter (@@name)
49116        if self.match_token(TokenType::SessionParameter) {
49117            let text = self.previous().text.clone();
49118            return Ok(Some(Expression::SessionParameter(Box::new(
49119                SessionParameter {
49120                    this: Box::new(Expression::Identifier(Identifier::new(text))),
49121                    kind: None,
49122                },
49123            ))));
49124        }
49125
49126        Ok(None)
49127    }
49128
49129    /// parse_paren - Ported from Python _parse_paren
49130    /// Parses parenthesized expressions: (expr), (select ...), or (a, b, c)
49131    #[allow(unused_variables, unused_mut)]
49132    pub fn parse_paren(&mut self) -> Result<Option<Expression>> {
49133        if !self.match_token(TokenType::LParen) {
49134            return Ok(None);
49135        }
49136
49137        // Check for empty tuple ()
49138        if self.match_token(TokenType::RParen) {
49139            return Ok(Some(Expression::Tuple(Box::new(Tuple {
49140                expressions: Vec::new(),
49141            }))));
49142        }
49143
49144        // Try to parse as subquery first
49145        // ClickHouse also allows (EXPLAIN ...) as subquery
49146        if self.check(TokenType::Select)
49147            || self.check(TokenType::With)
49148            || (matches!(
49149                self.config.dialect,
49150                Some(crate::dialects::DialectType::ClickHouse)
49151            ) && self.check(TokenType::Var)
49152                && self.peek().text.eq_ignore_ascii_case("EXPLAIN"))
49153        {
49154            let query = self.parse_statement()?;
49155            self.expect(TokenType::RParen)?;
49156            return Ok(Some(Expression::Subquery(Box::new(Subquery {
49157                this: query,
49158                alias: None,
49159                column_aliases: Vec::new(),
49160                order_by: None,
49161                limit: None,
49162                offset: None,
49163                lateral: false,
49164                modifiers_inside: true,
49165                trailing_comments: Vec::new(),
49166                distribute_by: None,
49167                sort_by: None,
49168                cluster_by: None,
49169                inferred_type: None,
49170            }))));
49171        }
49172
49173        // Parse comma-separated expressions
49174        let mut expressions = Vec::new();
49175        let mut trailing_comma = false;
49176        loop {
49177            match self.parse_expression() {
49178                Ok(expr) => expressions.push(expr),
49179                Err(_) => break,
49180            }
49181            if !self.match_token(TokenType::Comma) {
49182                break;
49183            }
49184            // ClickHouse: trailing comma makes a single-element tuple, e.g., (1,)
49185            if self.check(TokenType::RParen) {
49186                trailing_comma = true;
49187                break;
49188            }
49189        }
49190
49191        self.expect(TokenType::RParen)?;
49192
49193        // Single expression with trailing comma → tuple, e.g., (1,)
49194        if trailing_comma && expressions.len() == 1 {
49195            return Ok(Some(Expression::Tuple(Box::new(Tuple { expressions }))));
49196        }
49197
49198        // Single expression - return the unwrapped Paren
49199        if expressions.len() == 1 {
49200            return Ok(Some(Expression::Paren(Box::new(Paren {
49201                this: expressions.remove(0),
49202                trailing_comments: Vec::new(),
49203            }))));
49204        }
49205
49206        // Multiple expressions - return as tuple
49207        Ok(Some(Expression::Tuple(Box::new(Tuple { expressions }))))
49208    }
49209
49210    /// parse_partition - Parses PARTITION/SUBPARTITION clause
49211    /// Python: _parse_partition
49212    pub fn parse_partition(&mut self) -> Result<Option<Expression>> {
49213        // PARTITION_KEYWORDS = {"PARTITION", "SUBPARTITION"}
49214        if !self.match_texts(&["PARTITION", "SUBPARTITION"]) {
49215            return Ok(None);
49216        }
49217
49218        let subpartition = self.previous().text.eq_ignore_ascii_case("SUBPARTITION");
49219
49220        // Parse wrapped CSV of disjunction expressions
49221        if !self.match_token(TokenType::LParen) {
49222            // Without parentheses, still return a Partition with empty expressions
49223            return Ok(Some(Expression::Partition(Box::new(Partition {
49224                expressions: Vec::new(),
49225                subpartition,
49226            }))));
49227        }
49228
49229        let mut expressions = Vec::new();
49230        loop {
49231            if let Some(expr) = self.parse_disjunction()? {
49232                expressions.push(expr);
49233            } else {
49234                break;
49235            }
49236
49237            if !self.match_token(TokenType::Comma) {
49238                break;
49239            }
49240        }
49241
49242        self.match_token(TokenType::RParen);
49243
49244        Ok(Some(Expression::Partition(Box::new(Partition {
49245            expressions,
49246            subpartition,
49247        }))))
49248    }
49249
49250    /// parse_partition_and_order - Delegates to parse_partition_by
49251    #[allow(unused_variables, unused_mut)]
49252    pub fn parse_partition_and_order(&mut self) -> Result<Option<Expression>> {
49253        self.parse_partition_by()
49254    }
49255
49256    /// parse_partition_bound_spec - Implemented from Python _parse_partition_bound_spec
49257    /// Calls: parse_bitwise, parse_number
49258    #[allow(unused_variables, unused_mut)]
49259    pub fn parse_partition_bound_spec_legacy(&mut self) -> Result<Option<Expression>> {
49260        if self.match_text_seq(&["MINVALUE"]) {
49261            return Ok(Some(Expression::PartitionBoundSpec(Box::new(
49262                PartitionBoundSpec {
49263                    this: None,
49264                    expression: None,
49265                    from_expressions: None,
49266                    to_expressions: None,
49267                },
49268            ))));
49269        }
49270        if self.match_text_seq(&["MAXVALUE"]) {
49271            // Matched: MAXVALUE
49272            return Ok(None);
49273        }
49274        if self.match_text_seq(&["TO"]) {
49275            // Matched: TO
49276            return Ok(None);
49277        }
49278        Ok(None)
49279    }
49280
49281    /// parse_partition_by - Ported from Python _parse_partition_by
49282    /// Parses PARTITION BY expression list
49283    #[allow(unused_variables, unused_mut)]
49284    pub fn parse_partition_by(&mut self) -> Result<Option<Expression>> {
49285        if !self.match_keywords(&[TokenType::Partition, TokenType::By]) {
49286            return Ok(None);
49287        }
49288        let expressions = self.parse_expression_list()?;
49289        Ok(Some(Expression::Tuple(Box::new(Tuple { expressions }))))
49290    }
49291
49292    /// parse_partitioned_by - Parses PARTITIONED BY clause
49293    /// Python: _parse_partitioned_by
49294    pub fn parse_partitioned_by(&mut self) -> Result<Option<Expression>> {
49295        // Optionally match '='
49296        self.match_token(TokenType::Eq);
49297
49298        // Try to parse a schema first
49299        if let Some(schema) = self.parse_schema()? {
49300            return Ok(Some(Expression::PartitionedByProperty(Box::new(
49301                PartitionedByProperty {
49302                    this: Box::new(schema),
49303                },
49304            ))));
49305        }
49306
49307        // Fall back to bracket(field)
49308        if let Some(bracket) = self.parse_bracket()? {
49309            return Ok(Some(Expression::PartitionedByProperty(Box::new(
49310                PartitionedByProperty {
49311                    this: Box::new(bracket),
49312                },
49313            ))));
49314        }
49315
49316        // Try to parse a field directly
49317        if let Some(field) = self.parse_field()? {
49318            return Ok(Some(Expression::PartitionedByProperty(Box::new(
49319                PartitionedByProperty {
49320                    this: Box::new(field),
49321                },
49322            ))));
49323        }
49324
49325        Ok(None)
49326    }
49327
49328    /// parse_partitioned_by_bucket_or_truncate - Parses BUCKET or TRUNCATE partition transforms
49329    /// Python: _parse_partitioned_by_bucket_or_truncate
49330    /// Syntax: BUCKET(col, num_buckets) or TRUNCATE(col, width)
49331    /// Handles both Hive (num, col) and Trino (col, num) ordering, normalizes to (col, num)
49332    pub fn parse_partitioned_by_bucket_or_truncate(&mut self) -> Result<Option<Expression>> {
49333        // If no L_PAREN follows, this should be parsed as an identifier, not a function call
49334        if !self.check(TokenType::LParen) {
49335            // Retreat: go back one token (previous was BUCKET or TRUNCATE)
49336            if self.current > 0 {
49337                self.current -= 1;
49338            }
49339            return Ok(None);
49340        }
49341
49342        // Determine if it's BUCKET or TRUNCATE based on previous token
49343        let is_bucket = self.previous().text.eq_ignore_ascii_case("BUCKET");
49344
49345        // Parse wrapped arguments
49346        self.expect(TokenType::LParen)?;
49347        let mut args = Vec::new();
49348
49349        if !self.check(TokenType::RParen) {
49350            loop {
49351                // Try to parse primary or column
49352                if let Some(expr) = self.parse_primary_or_var()? {
49353                    args.push(expr);
49354                } else if let Some(col) = self.parse_column()? {
49355                    args.push(col);
49356                }
49357
49358                if !self.match_token(TokenType::Comma) {
49359                    break;
49360                }
49361            }
49362        }
49363        self.match_token(TokenType::RParen);
49364
49365        // Get first two arguments
49366        let (mut this, mut expr) = (args.get(0).cloned(), args.get(1).cloned());
49367
49368        // Normalize: if first arg is a Literal, swap (Hive uses (num, col), Trino uses (col, num))
49369        // We canonicalize to (col, num)
49370        if let Some(Expression::Literal(_)) = &this {
49371            std::mem::swap(&mut this, &mut expr);
49372        }
49373
49374        // Ensure we have both arguments
49375        let this_expr = this.unwrap_or(Expression::Null(Null));
49376        let expr_expr = expr.unwrap_or(Expression::Null(Null));
49377
49378        if is_bucket {
49379            Ok(Some(Expression::PartitionedByBucket(Box::new(
49380                PartitionedByBucket {
49381                    this: Box::new(this_expr),
49382                    expression: Box::new(expr_expr),
49383                },
49384            ))))
49385        } else {
49386            Ok(Some(Expression::PartitionByTruncate(Box::new(
49387                PartitionByTruncate {
49388                    this: Box::new(this_expr),
49389                    expression: Box::new(expr_expr),
49390                },
49391            ))))
49392        }
49393    }
49394
49395    /// parse_doris_partition_by_range_or_list - Parses Doris PARTITION BY RANGE/LIST syntax
49396    /// Handles:
49397    ///   PARTITION BY RANGE (`col`) (PARTITION name VALUES LESS THAN (val), ...)
49398    ///   PARTITION BY RANGE (`col`) (PARTITION name VALUES [(val1), (val2)), ...)
49399    ///   PARTITION BY RANGE (`col`) (FROM ('start') TO ('end') INTERVAL n UNIT)
49400    ///   PARTITION BY LIST (`col`) (PARTITION name VALUES IN (val1, val2), ...)
49401    fn parse_doris_partition_by_range_or_list(&mut self, kind: &str) -> Result<Expression> {
49402        // Parse partition column expressions: (`col1`, `col2`, ...) or (STR2DATE(col, fmt))
49403        // Use parse_wrapped_csv to handle function calls in partition columns
49404        let partition_expressions = self.parse_wrapped_csv()?;
49405
49406        // Check for partition definitions in parentheses
49407        let create_expressions = if self.check(TokenType::LParen) {
49408            self.skip(); // consume (
49409
49410            if kind == "LIST" {
49411                // Parse LIST partition definitions: PARTITION name VALUES IN (val1, val2), ...
49412                let partitions = self.parse_doris_list_partition_definitions()?;
49413                self.expect(TokenType::RParen)?;
49414                Some(Box::new(Expression::Tuple(Box::new(Tuple {
49415                    expressions: partitions,
49416                }))))
49417            } else {
49418                // RANGE: check for FROM (dynamic), START (StarRocks dynamic), or PARTITION (static)
49419                if self.check(TokenType::From) {
49420                    // Dynamic: FROM ('start') TO ('end') INTERVAL n UNIT
49421                    let dynamic_expr = self.parse_doris_dynamic_partition()?;
49422                    self.expect(TokenType::RParen)?;
49423                    Some(Box::new(dynamic_expr))
49424                } else if self.check(TokenType::Start) {
49425                    // StarRocks dynamic: START ('val') END ('val') EVERY (expr), ...
49426                    let mut dynamics = Vec::new();
49427                    loop {
49428                        if !self.check(TokenType::Start) {
49429                            break;
49430                        }
49431                        let dynamic_expr = self.parse_starrocks_start_end_every()?;
49432                        dynamics.push(dynamic_expr);
49433                        if !self.match_token(TokenType::Comma) {
49434                            break;
49435                        }
49436                    }
49437                    self.expect(TokenType::RParen)?;
49438                    Some(Box::new(Expression::Tuple(Box::new(Tuple {
49439                        expressions: dynamics,
49440                    }))))
49441                } else if self.check(TokenType::Partition) {
49442                    // Static: PARTITION name VALUES LESS THAN (val) or VALUES [(val1), (val2))
49443                    let partitions = self.parse_doris_range_partition_definitions()?;
49444                    self.expect(TokenType::RParen)?;
49445                    Some(Box::new(Expression::Tuple(Box::new(Tuple {
49446                        expressions: partitions,
49447                    }))))
49448                } else {
49449                    self.expect(TokenType::RParen)?;
49450                    None
49451                }
49452            }
49453        } else {
49454            None
49455        };
49456
49457        if kind == "LIST" {
49458            Ok(Expression::PartitionByListProperty(Box::new(
49459                PartitionByListProperty {
49460                    partition_expressions: partition_expressions.map(Box::new),
49461                    create_expressions,
49462                },
49463            )))
49464        } else {
49465            Ok(Expression::PartitionByRangeProperty(Box::new(
49466                PartitionByRangeProperty {
49467                    partition_expressions: partition_expressions.map(Box::new),
49468                    create_expressions,
49469                },
49470            )))
49471        }
49472    }
49473
49474    /// Parse Doris LIST partition definitions: PARTITION name VALUES IN (val1, val2), ...
49475    fn parse_doris_list_partition_definitions(&mut self) -> Result<Vec<Expression>> {
49476        let mut partitions = Vec::new();
49477        loop {
49478            if !self.match_token(TokenType::Partition) {
49479                break;
49480            }
49481            let name = self.parse_id_var()?.unwrap_or(Expression::Null(Null));
49482            self.match_text_seq(&["VALUES", "IN"]);
49483            let values = self.parse_wrapped_csv_expressions()?;
49484
49485            let part_list = Expression::PartitionList(Box::new(PartitionList {
49486                this: Box::new(name),
49487                expressions: values,
49488            }));
49489            partitions.push(Expression::Partition(Box::new(Partition {
49490                expressions: vec![part_list],
49491                subpartition: false,
49492            })));
49493
49494            if !self.match_token(TokenType::Comma) {
49495                break;
49496            }
49497        }
49498        Ok(partitions)
49499    }
49500
49501    /// Parse Doris RANGE partition definitions
49502    fn parse_doris_range_partition_definitions(&mut self) -> Result<Vec<Expression>> {
49503        let mut partitions = Vec::new();
49504        loop {
49505            if !self.match_token(TokenType::Partition) {
49506                break;
49507            }
49508            let name = self.parse_id_var()?.unwrap_or(Expression::Null(Null));
49509            self.match_text_seq(&["VALUES"]);
49510
49511            let part_range = if self.match_text_seq(&["LESS", "THAN"]) {
49512                // VALUES LESS THAN (val) or VALUES LESS THAN (MAXVALUE)
49513                let values = self.parse_wrapped_csv_expressions()?;
49514                Expression::PartitionRange(Box::new(PartitionRange {
49515                    this: Box::new(name),
49516                    expression: None,
49517                    expressions: values,
49518                }))
49519            } else if self.check(TokenType::LBracket) {
49520                // VALUES [(val1), (val2)) - note asymmetric brackets
49521                self.skip(); // consume [
49522                let mut value_tuples = Vec::new();
49523                loop {
49524                    let vals = self.parse_wrapped_csv_expressions()?;
49525                    // Wrap in a Tuple for each (val)
49526                    value_tuples.push(Expression::Tuple(Box::new(Tuple { expressions: vals })));
49527                    if !self.match_token(TokenType::Comma) {
49528                        break;
49529                    }
49530                }
49531                // Expect ) to close the asymmetric bracket
49532                self.expect(TokenType::RParen)?;
49533                Expression::PartitionRange(Box::new(PartitionRange {
49534                    this: Box::new(name),
49535                    expression: None,
49536                    expressions: value_tuples,
49537                }))
49538            } else {
49539                // Fallback: no values
49540                Expression::PartitionRange(Box::new(PartitionRange {
49541                    this: Box::new(name),
49542                    expression: None,
49543                    expressions: Vec::new(),
49544                }))
49545            };
49546
49547            partitions.push(Expression::Partition(Box::new(Partition {
49548                expressions: vec![part_range],
49549                subpartition: false,
49550            })));
49551
49552            if !self.match_token(TokenType::Comma) {
49553                break;
49554            }
49555        }
49556        Ok(partitions)
49557    }
49558
49559    /// Parse Doris dynamic partition: FROM ('start') TO ('end') INTERVAL n UNIT
49560    fn parse_doris_dynamic_partition(&mut self) -> Result<Expression> {
49561        self.expect(TokenType::From)?;
49562        let start = self.parse_wrapped_expression()?;
49563        self.expect(TokenType::To)?;
49564        let end = self.parse_wrapped_expression()?;
49565
49566        // Parse INTERVAL n UNIT
49567        let every = if self.match_token(TokenType::Interval) {
49568            let number = self.parse_expression()?;
49569            let unit = if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
49570                let unit_text = self.advance().text.to_ascii_uppercase();
49571                // Convert unit text to IntervalUnit
49572                let interval_unit = match unit_text.as_str() {
49573                    "YEAR" | "YEARS" => crate::expressions::IntervalUnit::Year,
49574                    "MONTH" | "MONTHS" => crate::expressions::IntervalUnit::Month,
49575                    "DAY" | "DAYS" => crate::expressions::IntervalUnit::Day,
49576                    "HOUR" | "HOURS" => crate::expressions::IntervalUnit::Hour,
49577                    "MINUTE" | "MINUTES" => crate::expressions::IntervalUnit::Minute,
49578                    "SECOND" | "SECONDS" => crate::expressions::IntervalUnit::Second,
49579                    _ => crate::expressions::IntervalUnit::Day, // Default fallback
49580                };
49581                Some(crate::expressions::IntervalUnitSpec::Simple {
49582                    unit: interval_unit,
49583                    use_plural: unit_text.ends_with('S'),
49584                })
49585            } else {
49586                None
49587            };
49588            Some(Box::new(Expression::Interval(Box::new(Interval {
49589                this: Some(number),
49590                unit,
49591            }))))
49592        } else {
49593            None
49594        };
49595
49596        Ok(Expression::PartitionByRangePropertyDynamic(Box::new(
49597            PartitionByRangePropertyDynamic {
49598                this: None,
49599                start: Some(Box::new(start)),
49600                end: Some(Box::new(end)),
49601                every,
49602                use_start_end: false,
49603            },
49604        )))
49605    }
49606
49607    /// Parse StarRocks START ('val') END ('val') EVERY (expr) syntax
49608    fn parse_starrocks_start_end_every(&mut self) -> Result<Expression> {
49609        self.expect(TokenType::Start)?;
49610        let start = self.parse_wrapped_expression()?;
49611        self.expect(TokenType::End)?;
49612        let end = self.parse_wrapped_expression()?;
49613
49614        // Parse EVERY (expr)
49615        let every = if self.match_identifier("EVERY") {
49616            self.expect(TokenType::LParen)?;
49617            let expr = self.parse_expression()?;
49618            self.expect(TokenType::RParen)?;
49619            Some(Box::new(expr))
49620        } else {
49621            None
49622        };
49623
49624        Ok(Expression::PartitionByRangePropertyDynamic(Box::new(
49625            PartitionByRangePropertyDynamic {
49626                this: None,
49627                start: Some(Box::new(start)),
49628                end: Some(Box::new(end)),
49629                every,
49630                use_start_end: true,
49631            },
49632        )))
49633    }
49634
49635    /// Parse wrapped comma-separated expressions: (expr, expr, ...)
49636    fn parse_wrapped_csv_expressions(&mut self) -> Result<Vec<Expression>> {
49637        self.expect(TokenType::LParen)?;
49638        let mut exprs = Vec::new();
49639        if !self.check(TokenType::RParen) {
49640            loop {
49641                // Check for MAXVALUE special keyword
49642                if self.match_token(TokenType::Maxvalue) {
49643                    exprs.push(Expression::Var(Box::new(Var {
49644                        this: "MAXVALUE".to_string(),
49645                    })));
49646                } else {
49647                    exprs.push(self.parse_expression()?);
49648                }
49649                if !self.match_token(TokenType::Comma) {
49650                    break;
49651                }
49652            }
49653        }
49654        self.expect(TokenType::RParen)?;
49655        Ok(exprs)
49656    }
49657
49658    /// Parse a single wrapped expression: (expr)
49659    fn parse_wrapped_expression(&mut self) -> Result<Expression> {
49660        self.expect(TokenType::LParen)?;
49661        let expr = self.parse_expression()?;
49662        self.expect(TokenType::RParen)?;
49663        Ok(expr)
49664    }
49665
49666    /// parse_partitioned_of - Implemented from Python _parse_partitioned_of
49667    #[allow(unused_variables, unused_mut)]
49668    pub fn parse_partitioned_of(&mut self) -> Result<Option<Expression>> {
49669        if self.match_text_seq(&["OF"]) {
49670            return Ok(Some(Expression::PartitionBoundSpec(Box::new(
49671                PartitionBoundSpec {
49672                    this: None,
49673                    expression: None,
49674                    from_expressions: None,
49675                    to_expressions: None,
49676                },
49677            ))));
49678        }
49679        if self.match_text_seq(&["FOR", "VALUES"]) {
49680            // Matched: FOR VALUES
49681            return Ok(None);
49682        }
49683        Ok(None)
49684    }
49685
49686    /// parse_period_for_system_time - Parses PERIOD FOR SYSTEM_TIME constraint
49687    /// Python: _parse_period_for_system_time
49688    /// Syntax: PERIOD FOR SYSTEM_TIME (start_col, end_col)
49689    pub fn parse_period_for_system_time(&mut self) -> Result<Option<Expression>> {
49690        // Check for SYSTEM_TIME / TIMESTAMP_SNAPSHOT token
49691        if !self.match_token(TokenType::TimestampSnapshot) {
49692            // Retreat: go back one token
49693            if self.current > 0 {
49694                self.current -= 1;
49695            }
49696            return Ok(None);
49697        }
49698
49699        // Parse wrapped id vars (two column names)
49700        let id_vars = self.parse_wrapped_id_vars()?;
49701
49702        // Extract the two columns from the tuple
49703        let (this, expression) = if let Some(Expression::Tuple(tuple)) = id_vars {
49704            let exprs = &tuple.expressions;
49705            (
49706                exprs.get(0).cloned().unwrap_or(Expression::Null(Null)),
49707                exprs.get(1).cloned().unwrap_or(Expression::Null(Null)),
49708            )
49709        } else {
49710            return Ok(None);
49711        };
49712
49713        Ok(Some(Expression::PeriodForSystemTimeConstraint(Box::new(
49714            PeriodForSystemTimeConstraint {
49715                this: Box::new(this),
49716                expression: Box::new(expression),
49717            },
49718        ))))
49719    }
49720
49721    /// parse_pipe_syntax_aggregate - Implemented from Python _parse_pipe_syntax_aggregate
49722    #[allow(unused_variables, unused_mut)]
49723    pub fn parse_pipe_syntax_aggregate(&mut self) -> Result<Option<Expression>> {
49724        if self.match_text_seq(&["AGGREGATE"]) {
49725            return Ok(Some(Expression::Select(Box::new(Select {
49726                expressions: Vec::new(),
49727                from: None,
49728                joins: Vec::new(),
49729                lateral_views: Vec::new(),
49730                prewhere: None,
49731                where_clause: None,
49732                group_by: None,
49733                having: None,
49734                qualify: None,
49735                order_by: None,
49736                distribute_by: None,
49737                cluster_by: None,
49738                sort_by: None,
49739                limit: None,
49740                offset: None,
49741                limit_by: None,
49742                fetch: None,
49743                distinct: false,
49744                distinct_on: None,
49745                top: None,
49746                with: None,
49747                sample: None,
49748                settings: None,
49749                format: None,
49750                windows: None,
49751                hint: None,
49752                connect: None,
49753                into: None,
49754                locks: Vec::new(),
49755                for_xml: Vec::new(),
49756                leading_comments: Vec::new(),
49757                post_select_comments: Vec::new(),
49758                kind: None,
49759                operation_modifiers: Vec::new(),
49760                qualify_after_window: false,
49761                option: None,
49762                exclude: None,
49763            }))));
49764        }
49765        if self.match_text_seq(&["GROUP", "AND"]) {
49766            // Matched: GROUP AND
49767            return Ok(None);
49768        }
49769        Ok(None)
49770    }
49771
49772    /// parse_pipe_syntax_aggregate_fields - Implemented from Python _parse_pipe_syntax_aggregate_fields
49773    /// Calls: parse_disjunction
49774    #[allow(unused_variables, unused_mut)]
49775    pub fn parse_pipe_syntax_aggregate_fields(&mut self) -> Result<Option<Expression>> {
49776        if self.match_text_seq(&["GROUP", "AND"]) {
49777            // Matched: GROUP AND
49778            return Ok(None);
49779        }
49780        Ok(None)
49781    }
49782
49783    /// parse_pipe_syntax_aggregate_group_order_by - Parses pipe syntax aggregate fields with grouping and ordering
49784    /// Python: _parse_pipe_syntax_aggregate_group_order_by
49785    /// Parses comma-separated aggregate fields and separates them into aggregates/groups and ORDER BY specs
49786    /// Returns a Tuple with two elements: (aggregates_and_groups, order_by_specs)
49787    pub fn parse_pipe_syntax_aggregate_group_order_by(&mut self) -> Result<Option<Expression>> {
49788        // Parse CSV of pipe syntax aggregate fields
49789        let mut aggregates_or_groups = Vec::new();
49790        let mut orders = Vec::new();
49791
49792        loop {
49793            if let Some(element) = self.parse_pipe_syntax_aggregate_fields()? {
49794                // Check if it's an Ordered expression (ORDER BY spec)
49795                match &element {
49796                    Expression::Ordered(ordered) => {
49797                        // Extract the inner expression, potentially adjusting for alias
49798                        let this = match &ordered.this {
49799                            Expression::Alias(alias) => {
49800                                // Use the alias name as an Identifier expression
49801                                Expression::Identifier(alias.alias.clone())
49802                            }
49803                            other => other.clone(),
49804                        };
49805                        // Add modified Ordered to orders
49806                        orders.push(Expression::Ordered(Box::new(Ordered {
49807                            this: this.clone(),
49808                            desc: ordered.desc,
49809                            nulls_first: ordered.nulls_first,
49810                            explicit_asc: ordered.explicit_asc,
49811                            with_fill: ordered.with_fill.clone(),
49812                        })));
49813                        aggregates_or_groups.push(this);
49814                    }
49815                    _ => {
49816                        aggregates_or_groups.push(element);
49817                    }
49818                }
49819            }
49820
49821            if !self.match_token(TokenType::Comma) {
49822                break;
49823            }
49824        }
49825
49826        if aggregates_or_groups.is_empty() && orders.is_empty() {
49827            return Ok(None);
49828        }
49829
49830        // Return a tuple with (aggregates_or_groups, orders)
49831        Ok(Some(Expression::Tuple(Box::new(Tuple {
49832            expressions: vec![
49833                Expression::Tuple(Box::new(Tuple {
49834                    expressions: aggregates_or_groups,
49835                })),
49836                Expression::Tuple(Box::new(Tuple {
49837                    expressions: orders,
49838                })),
49839            ],
49840        }))))
49841    }
49842
49843    /// parse_pipe_syntax_extend - Implemented from Python _parse_pipe_syntax_extend
49844    #[allow(unused_variables, unused_mut)]
49845    pub fn parse_pipe_syntax_extend(&mut self) -> Result<Option<Expression>> {
49846        if self.match_text_seq(&["EXTEND"]) {
49847            return Ok(Some(Expression::Select(Box::new(Select {
49848                expressions: Vec::new(),
49849                from: None,
49850                joins: Vec::new(),
49851                lateral_views: Vec::new(),
49852                prewhere: None,
49853                where_clause: None,
49854                group_by: None,
49855                having: None,
49856                qualify: None,
49857                order_by: None,
49858                distribute_by: None,
49859                cluster_by: None,
49860                sort_by: None,
49861                limit: None,
49862                offset: None,
49863                limit_by: None,
49864                fetch: None,
49865                distinct: false,
49866                distinct_on: None,
49867                top: None,
49868                with: None,
49869                sample: None,
49870                settings: None,
49871                format: None,
49872                windows: None,
49873                hint: None,
49874                connect: None,
49875                into: None,
49876                locks: Vec::new(),
49877                for_xml: Vec::new(),
49878                leading_comments: Vec::new(),
49879                post_select_comments: Vec::new(),
49880                kind: None,
49881                operation_modifiers: Vec::new(),
49882                qualify_after_window: false,
49883                option: None,
49884                exclude: None,
49885            }))));
49886        }
49887        Ok(None)
49888    }
49889
49890    /// parse_pipe_syntax_join - Parses JOIN in BigQuery pipe syntax
49891    /// Python: _parse_pipe_syntax_join
49892    /// Format: |> JOIN table ON condition
49893    pub fn parse_pipe_syntax_join(&mut self) -> Result<Option<Expression>> {
49894        // Parse the JOIN clause
49895        self.parse_join()
49896    }
49897
49898    /// parse_pipe_syntax_limit - Parses LIMIT/OFFSET in BigQuery pipe syntax
49899    /// Python: _parse_pipe_syntax_limit
49900    /// Format: |> LIMIT n [OFFSET m]
49901    pub fn parse_pipe_syntax_limit(&mut self) -> Result<Option<Expression>> {
49902        // Parse the LIMIT clause
49903        let limit = self.parse_limit()?;
49904
49905        // Parse optional OFFSET
49906        let offset = self.parse_offset()?;
49907
49908        // Combine into a tuple if both present
49909        match (limit, offset) {
49910            (Some(l), Some(o)) => Ok(Some(Expression::Tuple(Box::new(Tuple {
49911                expressions: vec![l, o],
49912            })))),
49913            (Some(l), None) => Ok(Some(l)),
49914            (None, Some(o)) => Ok(Some(o)),
49915            (None, None) => Ok(None),
49916        }
49917    }
49918
49919    /// parse_pipe_syntax_pivot - Parses PIVOT in BigQuery pipe syntax
49920    /// Python: _parse_pipe_syntax_pivot
49921    /// Format: |> PIVOT (agg_function FOR column IN (values))
49922    pub fn parse_pipe_syntax_pivot(&mut self) -> Result<Option<Expression>> {
49923        // For pipe syntax, we don't have a source yet - return pivot aggregation
49924        // The actual pivot parsing will be done in the query transformer
49925        self.parse_pivot_aggregation()
49926    }
49927
49928    /// parse_pipe_syntax_query - Parses a query with pipe syntax transformations
49929    /// Python: _parse_pipe_syntax_query
49930    /// Handles queries like: FROM table |> WHERE ... |> SELECT ... |> AGGREGATE ...
49931    pub fn parse_pipe_syntax_query(&mut self) -> Result<Option<Expression>> {
49932        // Start with a base query (could be a FROM clause or subquery)
49933        let mut query = self.parse_select_query()?;
49934
49935        if query.is_none() {
49936            return Ok(None);
49937        }
49938
49939        // Process pipe syntax chain: |> transform1 |> transform2 |> ...
49940        while self.match_token(TokenType::PipeGt) {
49941            let start_pos = self.current;
49942            let operator_text = self.peek().text.to_ascii_uppercase();
49943
49944            // Try to match known pipe syntax transforms
49945            let transform_result = match operator_text.as_str() {
49946                "WHERE" => {
49947                    self.skip();
49948                    self.parse_where()?
49949                }
49950                "SELECT" => {
49951                    self.skip();
49952                    self.parse_pipe_syntax_select()?
49953                }
49954                "AGGREGATE" => {
49955                    self.skip();
49956                    self.parse_pipe_syntax_aggregate()?
49957                }
49958                "EXTEND" => {
49959                    self.skip();
49960                    self.parse_pipe_syntax_extend()?
49961                }
49962                "LIMIT" => {
49963                    self.skip();
49964                    self.parse_pipe_syntax_limit()?
49965                }
49966                "JOIN" | "LEFT" | "RIGHT" | "INNER" | "OUTER" | "CROSS" | "FULL" => {
49967                    self.parse_pipe_syntax_join()?
49968                }
49969                "UNION" | "INTERSECT" | "EXCEPT" => self.parse_pipe_syntax_set_operator()?,
49970                "PIVOT" => {
49971                    self.skip();
49972                    self.parse_pipe_syntax_pivot()?
49973                }
49974                "TABLESAMPLE" => {
49975                    self.skip();
49976                    self.parse_pipe_syntax_tablesample()?
49977                }
49978                _ => {
49979                    // Try set operator or join as fallback
49980                    let set_op = self.parse_pipe_syntax_set_operator()?;
49981                    if set_op.is_some() {
49982                        set_op
49983                    } else {
49984                        let join_op = self.parse_pipe_syntax_join()?;
49985                        if join_op.is_some() {
49986                            join_op
49987                        } else {
49988                            // Unsupported operator, retreat and break
49989                            self.current = start_pos;
49990                            break;
49991                        }
49992                    }
49993                }
49994            };
49995
49996            // Apply transform to query
49997            if let Some(transform) = transform_result {
49998                // Wrap current query with transform in a PipeOperator
49999                let current_query = query.ok_or_else(|| {
50000                    self.parse_error("Expected base query before pipe syntax transform")
50001                })?;
50002                query = Some(Expression::PipeOperator(Box::new(PipeOperator {
50003                    this: current_query,
50004                    expression: transform,
50005                })));
50006            }
50007        }
50008
50009        Ok(query)
50010    }
50011
50012    /// parse_pipe_syntax_select - Parses SELECT in BigQuery pipe syntax
50013    /// Python: _parse_pipe_syntax_select
50014    /// Format: |> SELECT expressions
50015    pub fn parse_pipe_syntax_select(&mut self) -> Result<Option<Expression>> {
50016        // Parse the SELECT expressions without consuming the pipe
50017        let expressions = self.parse_expressions()?;
50018
50019        match expressions {
50020            Some(expr) => Ok(Some(expr)),
50021            None => Ok(Some(Expression::Star(Star {
50022                table: None,
50023                except: None,
50024                replace: None,
50025                rename: None,
50026                trailing_comments: Vec::new(),
50027                span: None,
50028            }))),
50029        }
50030    }
50031
50032    /// parse_pipe_syntax_set_operator - Parses set operation in BigQuery pipe syntax
50033    /// Python: _parse_pipe_syntax_set_operator
50034    /// Format: |> UNION ALL/INTERSECT/EXCEPT (subquery1, subquery2, ...)
50035    pub fn parse_pipe_syntax_set_operator(&mut self) -> Result<Option<Expression>> {
50036        // Try to parse as a set operation (UNION, INTERSECT, EXCEPT)
50037        if let Some(set_op) = self.parse_set_operations()? {
50038            Ok(Some(set_op))
50039        } else {
50040            Ok(None)
50041        }
50042    }
50043
50044    /// parse_pipe_syntax_tablesample - Parses TABLESAMPLE in BigQuery pipe syntax
50045    /// Python: _parse_pipe_syntax_tablesample
50046    /// Format: |> TABLESAMPLE SYSTEM (percent PERCENT)
50047    pub fn parse_pipe_syntax_tablesample(&mut self) -> Result<Option<Expression>> {
50048        // Parse the TABLESAMPLE clause
50049        self.parse_table_sample()
50050    }
50051
50052    /// parse_pivot_aggregation - Ported from Python _parse_pivot_aggregation
50053    /// Parses an aggregation function in PIVOT clause, optionally with alias
50054    #[allow(unused_variables, unused_mut)]
50055    pub fn parse_pivot_aggregation(&mut self) -> Result<Option<Expression>> {
50056        // Parse a function
50057        let func = self.parse_function()?;
50058
50059        if func.is_none() {
50060            // If previous token was a comma, silently return None
50061            if self.previous().token_type == TokenType::Comma {
50062                return Ok(None);
50063            }
50064            // Otherwise this could be an error, but we'll just return None
50065            return Ok(None);
50066        }
50067
50068        // Try to parse an alias for the function
50069        self.parse_alias_with_expr(func)
50070    }
50071
50072    /// parse_pivot_in - Parses the IN clause of a PIVOT
50073    /// Python: _parse_pivot_in
50074    /// Format: column IN (value1 [AS alias1], value2 [AS alias2], ...)
50075    pub fn parse_pivot_in(&mut self) -> Result<Option<Expression>> {
50076        // Parse the column being pivoted
50077        let value = self.parse_column()?;
50078        let value_expr = value.unwrap_or(Expression::Null(Null));
50079
50080        // Expect IN keyword
50081        if !self.match_token(TokenType::In) {
50082            return Err(self.parse_error("Expecting IN"));
50083        }
50084
50085        // Check if it's a parenthesized list or a field reference
50086        if self.match_token(TokenType::LParen) {
50087            // Check for ANY keyword
50088            let expressions = if self.match_text_seq(&["ANY"]) {
50089                // Parse PivotAny with optional ORDER BY
50090                let order = self.parse_order()?;
50091                vec![Expression::PivotAny(Box::new(PivotAny {
50092                    this: order.map(Box::new),
50093                }))]
50094            } else {
50095                // Parse comma-separated list of expressions, optionally aliased
50096                let mut exprs = Vec::new();
50097                loop {
50098                    if let Some(expr) = self.parse_select_or_expression()? {
50099                        // Check for alias
50100                        let final_expr = if self.match_token(TokenType::Alias) {
50101                            if let Some(alias) = self.parse_bitwise()? {
50102                                // Store the alias expression directly
50103                                Expression::PivotAlias(Box::new(PivotAlias { this: expr, alias }))
50104                            } else {
50105                                expr
50106                            }
50107                        } else {
50108                            expr
50109                        };
50110                        exprs.push(final_expr);
50111                    } else {
50112                        break;
50113                    }
50114                    if !self.match_token(TokenType::Comma) {
50115                        break;
50116                    }
50117                }
50118                exprs
50119            };
50120
50121            self.expect(TokenType::RParen)?;
50122
50123            Ok(Some(Expression::In(Box::new(In {
50124                this: value_expr,
50125                expressions,
50126                query: None,
50127                not: false,
50128                global: false,
50129                unnest: None,
50130                is_field: false,
50131            }))))
50132        } else {
50133            // Parse as a field reference: IN field_name
50134            let field = self.parse_id_var()?;
50135            // Convert field to expression and add to expressions
50136            let expressions = if let Some(f) = field {
50137                vec![f]
50138            } else {
50139                Vec::new()
50140            };
50141            Ok(Some(Expression::In(Box::new(In {
50142                this: value_expr,
50143                expressions,
50144                query: None,
50145                not: false,
50146                global: false,
50147                unnest: None,
50148                is_field: true,
50149            }))))
50150        }
50151    }
50152
50153    /// parse_pivots - Ported from Python _parse_pivots
50154    /// Parses one or more PIVOT/UNPIVOT clauses attached to a source expression
50155    /// Uses the existing parse_pivot/parse_unpivot methods
50156    pub fn parse_pivots_for_source(&mut self, source: Expression) -> Result<Option<Expression>> {
50157        let mut result = source;
50158
50159        loop {
50160            if self.match_token(TokenType::Pivot) {
50161                result = self.parse_pivot(result)?;
50162            } else if self.match_texts(&["UNPIVOT"]) {
50163                result = self.parse_unpivot(result)?;
50164            } else {
50165                break;
50166            }
50167        }
50168
50169        // Return None if no pivots were parsed
50170        if matches!(result, Expression::Null(_)) {
50171            Ok(None)
50172        } else {
50173            Ok(Some(result))
50174        }
50175    }
50176
50177    /// parse_placeholder - Parse placeholder token (? or :name)
50178    /// Python: if self._match_set(self.PLACEHOLDER_PARSERS): return placeholder
50179    pub fn parse_placeholder(&mut self) -> Result<Option<Expression>> {
50180        // Match positional placeholder (?)
50181        if self.match_token(TokenType::Placeholder) {
50182            return Ok(Some(Expression::Placeholder(Placeholder { index: None })));
50183        }
50184        // Match colon placeholder (:name) - handled by Parameter token
50185        if self.match_token(TokenType::Parameter) {
50186            let text = self.previous().text.clone();
50187            return Ok(Some(Expression::Parameter(Box::new(Parameter {
50188                name: Some(text),
50189                index: None,
50190                style: ParameterStyle::Colon,
50191                quoted: false,
50192                string_quoted: false,
50193                expression: None,
50194            }))));
50195        }
50196        Ok(None)
50197    }
50198
50199    /// Parse ClickHouse query parameter syntax: {name: Type}
50200    fn parse_clickhouse_braced_parameter(&mut self) -> Result<Option<Expression>> {
50201        if !matches!(
50202            self.config.dialect,
50203            Some(crate::dialects::DialectType::ClickHouse)
50204        ) {
50205            return Ok(None);
50206        }
50207        if !self.check(TokenType::LBrace) {
50208            return Ok(None);
50209        }
50210
50211        let start = self.current;
50212        self.skip(); // consume {
50213
50214        if !(self.is_identifier_token() || self.is_safe_keyword_as_identifier()) {
50215            self.current = start;
50216            return Ok(None);
50217        }
50218        let name = self.advance().text.clone();
50219
50220        if !self.match_token(TokenType::Colon) {
50221            self.current = start;
50222            return Ok(None);
50223        }
50224
50225        let kind_start = self.current;
50226        let mut paren_depth = 0usize;
50227        let mut bracket_depth = 0usize;
50228
50229        while !self.is_at_end() {
50230            let token_type = self.peek().token_type;
50231            match token_type {
50232                TokenType::LParen => {
50233                    paren_depth += 1;
50234                    self.skip();
50235                }
50236                TokenType::RParen => {
50237                    if paren_depth == 0 {
50238                        break;
50239                    }
50240                    paren_depth -= 1;
50241                    self.skip();
50242                }
50243                TokenType::LBracket => {
50244                    bracket_depth += 1;
50245                    self.skip();
50246                }
50247                TokenType::RBracket => {
50248                    if bracket_depth == 0 {
50249                        break;
50250                    }
50251                    bracket_depth -= 1;
50252                    self.skip();
50253                }
50254                TokenType::RBrace => {
50255                    if paren_depth == 0 && bracket_depth == 0 {
50256                        break;
50257                    }
50258                    self.skip();
50259                }
50260                _ => {
50261                    self.skip();
50262                }
50263            }
50264        }
50265
50266        if self.current <= kind_start || !self.match_token(TokenType::RBrace) {
50267            return Err(self.parse_error("Expected } in ClickHouse query parameter"));
50268        }
50269
50270        let kind = self
50271            .tokens_to_sql(kind_start, self.current - 1)
50272            .trim()
50273            .to_string();
50274        if kind.is_empty() {
50275            return Err(self.parse_error("Expected parameter kind in ClickHouse query parameter"));
50276        }
50277
50278        Ok(Some(Expression::Parameter(Box::new(Parameter {
50279            name: Some(name),
50280            index: None,
50281            style: ParameterStyle::Brace,
50282            quoted: false,
50283            string_quoted: false,
50284            expression: Some(kind),
50285        }))))
50286    }
50287
50288    /// parse_position - Ported from Python _parse_position
50289    /// Parses POSITION function: POSITION(substr IN str) or POSITION(needle, haystack, start)
50290    #[allow(unused_variables, unused_mut)]
50291    pub fn parse_position(&mut self) -> Result<Option<Expression>> {
50292        // Parse comma-separated arguments first
50293        let mut args: Vec<Expression> = Vec::new();
50294
50295        match self.parse_bitwise() {
50296            Ok(Some(expr)) => {
50297                let expr = self.maybe_clickhouse_alias(expr);
50298                let expr = self.try_clickhouse_func_arg_alias(expr);
50299                args.push(expr);
50300            }
50301            Ok(None) => return Ok(None),
50302            Err(e) => return Err(e),
50303        }
50304
50305        // Check for IN keyword (SQL standard syntax: POSITION(substr IN str))
50306        if self.match_token(TokenType::In) {
50307            match self.parse_bitwise() {
50308                Ok(Some(haystack)) => {
50309                    let haystack = self.maybe_clickhouse_alias(haystack);
50310                    let haystack = self.try_clickhouse_func_arg_alias(haystack);
50311                    return Ok(Some(Expression::StrPosition(Box::new(StrPosition {
50312                        this: Box::new(haystack),
50313                        substr: Some(Box::new(args.remove(0))),
50314                        position: None,
50315                        occurrence: None,
50316                    }))));
50317                }
50318                Ok(None) => {
50319                    return Err(self.parse_error("Expected expression after IN in POSITION"))
50320                }
50321                Err(e) => return Err(e),
50322            }
50323        }
50324
50325        // Parse comma-separated additional arguments
50326        while self.match_token(TokenType::Comma) {
50327            match self.parse_bitwise() {
50328                Ok(Some(expr)) => {
50329                    let expr = self.maybe_clickhouse_alias(expr);
50330                    let expr = self.try_clickhouse_func_arg_alias(expr);
50331                    args.push(expr);
50332                }
50333                Ok(None) => break,
50334                Err(e) => return Err(e),
50335            }
50336        }
50337
50338        // Function syntax: POSITION(needle, haystack, start?) or ClickHouse POSITION(haystack, needle, start?)
50339        let position = args.get(2).cloned();
50340        let (haystack, needle) = if matches!(
50341            self.config.dialect,
50342            Some(crate::dialects::DialectType::ClickHouse)
50343        ) {
50344            (args.get(0).cloned(), args.get(1).cloned())
50345        } else {
50346            (args.get(1).cloned(), args.get(0).cloned())
50347        };
50348
50349        Ok(Some(Expression::StrPosition(Box::new(StrPosition {
50350            this: Box::new(
50351                haystack.unwrap_or_else(|| Expression::Literal(Literal::String("".to_string()))),
50352            ),
50353            substr: needle.map(Box::new),
50354            position: position.map(Box::new),
50355            occurrence: None,
50356        }))))
50357    }
50358
50359    /// parse_prewhere - Ported from Python _parse_prewhere
50360    /// Parses PREWHERE clause (ClickHouse specific)
50361    #[allow(unused_variables, unused_mut)]
50362    pub fn parse_prewhere(&mut self) -> Result<Option<Expression>> {
50363        if !self.match_token(TokenType::Prewhere) {
50364            return Ok(None);
50365        }
50366        // Parse the condition expression
50367        let condition = self.parse_expression()?;
50368        Ok(Some(Expression::PreWhere(Box::new(PreWhere {
50369            this: condition,
50370        }))))
50371    }
50372
50373    /// parse_primary_key - Parses PRIMARY KEY constraint
50374    /// Python: _parse_primary_key
50375    /// Can return either PrimaryKeyColumnConstraint (column-level) or PrimaryKey (table-level)
50376    pub fn parse_primary_key(&mut self) -> Result<Option<Expression>> {
50377        self.parse_primary_key_impl(false, false)
50378    }
50379
50380    /// Implementation of parse_primary_key with options
50381    pub fn parse_primary_key_impl(
50382        &mut self,
50383        wrapped_optional: bool,
50384        in_props: bool,
50385    ) -> Result<Option<Expression>> {
50386        // Check for ASC/DESC
50387        let desc = if self.match_token(TokenType::Asc) {
50388            false
50389        } else if self.match_token(TokenType::Desc) {
50390            true
50391        } else {
50392            false
50393        };
50394
50395        // Parse optional constraint name (if current token is identifier and next is L_PAREN)
50396        let this = if (self.check(TokenType::Identifier) || self.check(TokenType::Var))
50397            && self.check_next(TokenType::LParen)
50398        {
50399            self.parse_id_var()?
50400        } else {
50401            None
50402        };
50403
50404        // If not in_props and no L_PAREN ahead, return column-level constraint
50405        if !in_props && !self.check(TokenType::LParen) {
50406            let options = self.parse_key_constraint_options_list()?;
50407            return Ok(Some(Expression::PrimaryKeyColumnConstraint(Box::new(
50408                PrimaryKeyColumnConstraint {
50409                    desc: if desc {
50410                        Some(Box::new(Expression::Boolean(BooleanLiteral {
50411                            value: true,
50412                        })))
50413                    } else {
50414                        None
50415                    },
50416                    options,
50417                },
50418            ))));
50419        }
50420
50421        // Parse table-level PRIMARY KEY (column_list)
50422        let expressions = if self.match_token(TokenType::LParen) {
50423            let mut exprs = Vec::new();
50424            loop {
50425                if let Some(part) = self.parse_primary_key_part()? {
50426                    exprs.push(part);
50427                }
50428                if !self.match_token(TokenType::Comma) {
50429                    break;
50430                }
50431            }
50432            self.expect(TokenType::RParen)?;
50433            exprs
50434        } else if wrapped_optional {
50435            Vec::new()
50436        } else {
50437            return Err(self.parse_error("Expected '(' for PRIMARY KEY column list"));
50438        };
50439
50440        // Parse INCLUDE clause for covering index
50441        let include = self.parse_index_params()?;
50442
50443        // Parse constraint options
50444        let options = self.parse_key_constraint_options_list()?;
50445
50446        Ok(Some(Expression::PrimaryKey(Box::new(PrimaryKey {
50447            this: this.map(Box::new),
50448            expressions,
50449            options,
50450            include: include.map(Box::new),
50451        }))))
50452    }
50453
50454    /// Parse key constraint options as a list of expressions
50455    fn parse_key_constraint_options_list(&mut self) -> Result<Vec<Expression>> {
50456        let mut options = Vec::new();
50457
50458        loop {
50459            if self.is_at_end() {
50460                break;
50461            }
50462
50463            if self.match_token(TokenType::On) {
50464                // Parse ON DELETE/UPDATE action
50465                let on_what = if !self.is_at_end() {
50466                    let token = self.advance();
50467                    token.text.clone()
50468                } else {
50469                    break;
50470                };
50471
50472                let action = if self.match_text_seq(&["NO", "ACTION"]) {
50473                    "NO ACTION"
50474                } else if self.match_text_seq(&["CASCADE"]) {
50475                    "CASCADE"
50476                } else if self.match_text_seq(&["RESTRICT"]) {
50477                    "RESTRICT"
50478                } else if self.match_token(TokenType::Set) && self.match_token(TokenType::Null) {
50479                    "SET NULL"
50480                } else if self.match_token(TokenType::Set) && self.match_token(TokenType::Default) {
50481                    "SET DEFAULT"
50482                } else {
50483                    break;
50484                };
50485
50486                options.push(Expression::Var(Box::new(Var {
50487                    this: format!("ON {} {}", on_what, action),
50488                })));
50489            } else if self.match_text_seq(&["NOT", "ENFORCED"]) {
50490                options.push(Expression::Var(Box::new(Var {
50491                    this: "NOT ENFORCED".to_string(),
50492                })));
50493            } else if self.match_text_seq(&["DEFERRABLE"]) {
50494                options.push(Expression::Var(Box::new(Var {
50495                    this: "DEFERRABLE".to_string(),
50496                })));
50497            } else if self.match_text_seq(&["INITIALLY", "DEFERRED"]) {
50498                options.push(Expression::Var(Box::new(Var {
50499                    this: "INITIALLY DEFERRED".to_string(),
50500                })));
50501            } else if self.match_text_seq(&["NORELY"]) {
50502                options.push(Expression::Var(Box::new(Var {
50503                    this: "NORELY".to_string(),
50504                })));
50505            } else if self.match_text_seq(&["RELY"]) {
50506                options.push(Expression::Var(Box::new(Var {
50507                    this: "RELY".to_string(),
50508                })));
50509            } else {
50510                break;
50511            }
50512        }
50513
50514        Ok(options)
50515    }
50516
50517    /// parse_primary_key_part - Delegates to parse_field
50518    #[allow(unused_variables, unused_mut)]
50519    pub fn parse_primary_key_part(&mut self) -> Result<Option<Expression>> {
50520        // ClickHouse: PRIMARY KEY can contain full expressions (e.g., t.a, c0 IN (SELECT 1))
50521        if matches!(
50522            self.config.dialect,
50523            Some(crate::dialects::DialectType::ClickHouse)
50524        ) {
50525            return self.parse_expression().map(Some);
50526        }
50527        if (self.is_identifier_token() || self.is_safe_keyword_as_identifier())
50528            && self.check_next(TokenType::LParen)
50529        {
50530            return self.parse_expression().map(Some);
50531        }
50532        if let Some(field) = self.parse_field()? {
50533            Ok(Some(field))
50534        } else {
50535            self.parse_expression().map(Some)
50536        }
50537    }
50538
50539    /// parse_primary_or_var - Parses a primary expression or variable
50540    /// Python: _parse_primary_or_var
50541    /// Returns: parse_primary() or parse_var(any_token=True)
50542    pub fn parse_primary_or_var(&mut self) -> Result<Option<Expression>> {
50543        // First try to parse a primary expression
50544        let saved_pos = self.current;
50545        match self.parse_primary() {
50546            Ok(expr) => return Ok(Some(expr)),
50547            Err(_) => {
50548                // Reset position and try parse_var
50549                self.current = saved_pos;
50550            }
50551        }
50552
50553        // Fall back to parsing a variable
50554        self.parse_var()
50555    }
50556
50557    /// parse_procedure_option - Implemented from Python _parse_procedure_option
50558    #[allow(unused_variables, unused_mut)]
50559    pub fn parse_procedure_option(&mut self) -> Result<Option<Expression>> {
50560        if self.match_text_seq(&["EXECUTE", "AS"]) {
50561            // Matched: EXECUTE AS
50562            return Ok(None);
50563        }
50564        Ok(None)
50565    }
50566
50567    /// parse_projections - Delegates to parse_expressions
50568    #[allow(unused_variables, unused_mut)]
50569    pub fn parse_projections(&mut self) -> Result<Option<Expression>> {
50570        self.parse_expressions()
50571    }
50572
50573    /// parse_properties - Parses table/column properties
50574    /// Python: _parse_properties
50575    /// Collects a list of properties using parse_property
50576    pub fn parse_properties(&mut self) -> Result<Option<Expression>> {
50577        self.parse_properties_impl(None)
50578    }
50579
50580    /// Implementation of parse_properties with before option
50581    pub fn parse_properties_impl(&mut self, before: Option<bool>) -> Result<Option<Expression>> {
50582        let mut properties = Vec::new();
50583
50584        loop {
50585            let prop = if before == Some(true) {
50586                self.parse_property_before()?
50587            } else {
50588                self.parse_property()?
50589            };
50590
50591            if let Some(p) = prop {
50592                properties.push(p);
50593            } else {
50594                break;
50595            }
50596        }
50597
50598        if properties.is_empty() {
50599            Ok(None)
50600        } else {
50601            Ok(Some(Expression::Properties(Box::new(Properties {
50602                expressions: properties,
50603            }))))
50604        }
50605    }
50606
50607    /// parse_property - Implemented from Python _parse_property
50608    /// Calls: parse_bitwise, parse_column, parse_sequence_properties
50609    #[allow(unused_variables, unused_mut)]
50610    pub fn parse_property(&mut self) -> Result<Option<Expression>> {
50611        if self.match_text_seq(&["COMPOUND", "SORTKEY"]) {
50612            return Ok(Some(Expression::Identifier(Identifier {
50613                name: String::new(),
50614                quoted: false,
50615                trailing_comments: Vec::new(),
50616                span: None,
50617            })));
50618        }
50619        if self.match_text_seq(&["SQL", "SECURITY"]) {
50620            // Matched: SQL SECURITY
50621            return Ok(None);
50622        }
50623        if self.match_texts(&["DEFINER", "INVOKER"]) {
50624            // Matched one of: DEFINER, INVOKER
50625            return Ok(None);
50626        }
50627        Ok(None)
50628    }
50629
50630    /// parse_on_cluster_clause - Parse ClickHouse ON CLUSTER clause
50631    fn parse_on_cluster_clause(&mut self) -> Result<Option<OnCluster>> {
50632        if !matches!(
50633            self.config.dialect,
50634            Some(crate::dialects::DialectType::ClickHouse)
50635        ) {
50636            return Ok(None);
50637        }
50638
50639        let start = self.current;
50640        if !self.match_token(TokenType::On) {
50641            return Ok(None);
50642        }
50643
50644        if !self.match_token(TokenType::Cluster) {
50645            self.current = start;
50646            return Ok(None);
50647        }
50648
50649        let this = if self.check(TokenType::String) {
50650            let value = self.expect_string()?;
50651            Expression::Literal(Literal::String(value))
50652        } else if let Some(id_expr) = self.parse_id_var()? {
50653            id_expr
50654        } else if self.is_safe_keyword_as_identifier() {
50655            let name = self.advance().text;
50656            Expression::Identifier(Identifier {
50657                name,
50658                quoted: false,
50659                trailing_comments: Vec::new(),
50660                span: None,
50661            })
50662        } else {
50663            return Err(self.parse_error("Expected cluster name after ON CLUSTER"));
50664        };
50665
50666        Ok(Some(OnCluster {
50667            this: Box::new(this),
50668        }))
50669    }
50670
50671    /// parse_clickhouse_table_properties - Parse ClickHouse table properties after column defs
50672    fn parse_clickhouse_table_properties(
50673        &mut self,
50674        properties: &mut Vec<Expression>,
50675    ) -> Result<()> {
50676        loop {
50677            if self.match_identifier("ENGINE") {
50678                self.match_token(TokenType::Eq);
50679                let engine = self.parse_clickhouse_engine_expression()?;
50680                properties.push(Expression::EngineProperty(Box::new(EngineProperty {
50681                    this: Box::new(engine),
50682                })));
50683                continue;
50684            }
50685
50686            if self.match_token(TokenType::Order) {
50687                self.expect(TokenType::By)?;
50688                let order_by = if matches!(
50689                    self.config.dialect,
50690                    Some(crate::dialects::DialectType::ClickHouse)
50691                ) && self.match_token(TokenType::LParen)
50692                {
50693                    // ClickHouse: ORDER BY (col1 [ASC|DESC], col2 [ASC|DESC], ...)
50694                    // or ORDER BY () for no ordering
50695                    if self.check(TokenType::RParen) {
50696                        self.skip();
50697                        OrderBy {
50698                            expressions: vec![Ordered::asc(Expression::Tuple(Box::new(Tuple {
50699                                expressions: Vec::new(),
50700                            })))],
50701                            siblings: false,
50702                            comments: Vec::new(),
50703                        }
50704                    } else {
50705                        // Parse all expressions inside the parentheses
50706                        let mut inner_exprs = Vec::new();
50707                        loop {
50708                            let expr = self.parse_expression()?;
50709                            inner_exprs.push(expr);
50710                            if !self.match_token(TokenType::Comma) {
50711                                break;
50712                            }
50713                        }
50714                        self.expect(TokenType::RParen)?;
50715                        // Wrap in a Tuple for multi-expr, Paren for single-expr
50716                        let wrapper = if inner_exprs.len() == 1 {
50717                            Expression::Paren(Box::new(Paren {
50718                                this: inner_exprs.into_iter().next().unwrap(),
50719                                trailing_comments: Vec::new(),
50720                            }))
50721                        } else {
50722                            Expression::Tuple(Box::new(Tuple {
50723                                expressions: inner_exprs,
50724                            }))
50725                        };
50726                        OrderBy {
50727                            expressions: vec![Ordered::asc(wrapper)],
50728                            siblings: false,
50729                            comments: Vec::new(),
50730                        }
50731                    }
50732                } else {
50733                    self.parse_order_by()?
50734                };
50735                properties.push(Expression::OrderBy(Box::new(order_by)));
50736                continue;
50737            }
50738
50739            if self.match_token(TokenType::Partition) {
50740                self.expect(TokenType::By)?;
50741                if self.check(TokenType::Order) && self.check_next(TokenType::By) {
50742                    return Err(self.parse_error("Expected expression after PARTITION BY"));
50743                }
50744                let expr = self
50745                    .parse_assignment()?
50746                    .ok_or_else(|| self.parse_error("Expected expression after PARTITION BY"))?;
50747                properties.push(Expression::PartitionedByProperty(Box::new(
50748                    PartitionedByProperty {
50749                        this: Box::new(expr),
50750                    },
50751                )));
50752                continue;
50753            }
50754
50755            if self.match_token(TokenType::PrimaryKey) {
50756                // ClickHouse supports PRIMARY KEY id and PRIMARY KEY (id, ...)
50757                let _ = self.match_token(TokenType::Key);
50758                if self.check(TokenType::LParen) {
50759                    if let Some(pk) = self.parse_primary_key_impl(false, true)? {
50760                        properties.push(pk);
50761                    }
50762                } else if let Some(expr) = self.parse_conjunction()? {
50763                    // ClickHouse: PRIMARY KEY expr (e.g., PRIMARY KEY tuple(), PRIMARY KEY id)
50764                    let mut exprs = vec![expr];
50765                    while self.match_token(TokenType::Comma) {
50766                        if let Some(next_expr) = self.parse_field()? {
50767                            exprs.push(next_expr);
50768                        } else {
50769                            break;
50770                        }
50771                    }
50772                    properties.push(Expression::PrimaryKey(Box::new(PrimaryKey {
50773                        this: None,
50774                        expressions: exprs,
50775                        options: Vec::new(),
50776                        include: None,
50777                    })));
50778                } else {
50779                    return Err(self.parse_error("Expected expression after PRIMARY KEY"));
50780                }
50781                continue;
50782            }
50783
50784            if self.match_token(TokenType::Sample) {
50785                let _ = self.match_token(TokenType::By);
50786                let expr = self.parse_expression()?;
50787                properties.push(Expression::SampleProperty(Box::new(SampleProperty {
50788                    this: Box::new(expr),
50789                })));
50790                continue;
50791            }
50792
50793            if self.match_token(TokenType::Settings) {
50794                let mut settings = Vec::new();
50795                loop {
50796                    settings.push(self.parse_expression()?);
50797                    if !self.match_token(TokenType::Comma) {
50798                        break;
50799                    }
50800                }
50801                properties.push(Expression::SettingsProperty(Box::new(SettingsProperty {
50802                    expressions: settings,
50803                })));
50804                continue;
50805            }
50806
50807            if self.match_token(TokenType::Comment) {
50808                let comment_expr = if self.check(TokenType::String) {
50809                    Expression::Literal(Literal::String(self.expect_string()?))
50810                } else {
50811                    self.parse_expression()?
50812                };
50813                properties.push(Expression::SchemaCommentProperty(Box::new(
50814                    SchemaCommentProperty {
50815                        this: Box::new(comment_expr),
50816                    },
50817                )));
50818                continue;
50819            }
50820
50821            // TTL time_column + INTERVAL '1' MONTH [DELETE|RECOMPRESS|TO DISK|TO VOLUME] [WHERE ...]
50822            if self.match_identifier("TTL") {
50823                if let Some(ttl_expr) = self.parse_ttl()? {
50824                    properties.push(ttl_expr);
50825                }
50826                continue;
50827            }
50828
50829            if self.match_identifier("SOURCE") {
50830                if let Some(prop) = self.parse_dict_property("SOURCE")? {
50831                    properties.push(prop);
50832                }
50833                continue;
50834            }
50835
50836            if self.match_identifier("LAYOUT") {
50837                if let Some(prop) = self.parse_dict_property("LAYOUT")? {
50838                    properties.push(prop);
50839                }
50840                continue;
50841            }
50842
50843            if self.match_identifier("LIFETIME") {
50844                if let Some(range) = self.parse_dict_range("LIFETIME")? {
50845                    properties.push(range);
50846                }
50847                continue;
50848            }
50849
50850            if self.match_identifier("RANGE") || self.match_token(TokenType::Range) {
50851                if let Some(range) = self.parse_dict_range("RANGE")? {
50852                    properties.push(range);
50853                }
50854                continue;
50855            }
50856
50857            break;
50858        }
50859
50860        Ok(())
50861    }
50862
50863    /// ClickHouse implicit alias in function arguments: `expr identifier` (without AS keyword).
50864    /// The token after the alias must be a delimiter (comma, RParen, FROM, FOR, AS).
50865    fn try_clickhouse_implicit_alias(&mut self, expr: Expression) -> Expression {
50866        if !matches!(
50867            self.config.dialect,
50868            Some(crate::dialects::DialectType::ClickHouse)
50869        ) {
50870            return expr;
50871        }
50872        if self.check(TokenType::Var) || self.check(TokenType::Identifier) {
50873            let next_after = self.peek_nth(1).map(|t| t.token_type);
50874            let is_delimiter = matches!(
50875                next_after,
50876                Some(TokenType::Comma)
50877                    | Some(TokenType::RParen)
50878                    | Some(TokenType::From)
50879                    | Some(TokenType::For)
50880                    | Some(TokenType::As)
50881            );
50882            if is_delimiter {
50883                let alias_token = self.advance();
50884                let alias_name = alias_token.text.clone();
50885                return Expression::Alias(Box::new(crate::expressions::Alias::new(
50886                    expr,
50887                    Identifier::new(alias_name),
50888                )));
50889            }
50890        }
50891        expr
50892    }
50893
50894    /// ClickHouse alias in function arguments: handles both implicit (`expr identifier`)
50895    /// and explicit (`expr AS identifier`) aliases. Use this in special function parsers
50896    /// (SUBSTRING, TRIM, EXTRACT) but NOT in CAST (which has its own AS handling).
50897    /// Normalize TSQL date part aliases (e.g., dd -> DAY, yy -> YEAR, etc.)
50898    fn normalize_tsql_date_part(&self, expr: Expression) -> Expression {
50899        let name = match &expr {
50900            Expression::Var(v) => Some(v.this.to_ascii_uppercase()),
50901            Expression::Column(c) if c.table.is_none() => Some(c.name.name.to_ascii_uppercase()),
50902            Expression::Identifier(id) => Some(id.name.to_ascii_uppercase()),
50903            _ => None,
50904        };
50905        if let Some(name) = name {
50906            let mapped = match name.as_str() {
50907                "YY" | "YYY" | "YYYY" | "YR" | "YEARS" | "YRS" => "YEAR",
50908                "MM" | "MON" | "MONS" | "MONTHS" | "M" => "MONTH",
50909                "D" | "DD" | "DAYS" | "DAYOFMONTH" => "DAY",
50910                "DOW" | "DW" | "WEEKDAY" => "DAYOFWEEK",
50911                "DOY" | "DY" | "Y" => "DAYOFYEAR",
50912                "W" | "WK" | "WEEKOFYEAR" | "WOY" | "WY" | "WW" => "WEEK",
50913                "Q" | "QTR" | "QTRS" | "QUARTERS" | "QQ" => "QUARTER",
50914                "H" | "HH" | "HR" | "HOURS" | "HRS" => "HOUR",
50915                "MI" | "MIN" | "MINUTES" | "MINS" | "N" => "MINUTE",
50916                "S" | "SEC" | "SECONDS" | "SECS" | "SS" => "SECOND",
50917                "MS" | "MSEC" | "MSECS" | "MSECOND" | "MSECONDS" | "MILLISEC" | "MILLISECS"
50918                | "MILLISECON" | "MILLISECONDS" => "MILLISECOND",
50919                "US" | "USEC" | "USECS" | "MICROSEC" | "MICROSECS" | "USECOND" | "USECONDS"
50920                | "MICROSECONDS" | "MCS" => "MICROSECOND",
50921                "NS" | "NSEC" | "NANOSEC" | "NSECOND" | "NSECONDS" | "NANOSECS" => "NANOSECOND",
50922                "TZH" => "TIMEZONE_HOUR",
50923                "TZM" | "TZOFFSET" | "TZ" => "TIMEZONE_MINUTE",
50924                "DEC" | "DECS" | "DECADES" => "DECADE",
50925                "MIL" | "MILS" | "MILLENIA" => "MILLENNIUM",
50926                "C" | "CENT" | "CENTS" | "CENTURIES" => "CENTURY",
50927                "ISOWK" | "ISOWW" | "ISO_WEEK" | "WEEKOFYEARISO" | "WEEKOFYEAR_ISO"
50928                | "WEEK_ISO" => "WEEKISO",
50929                _ => return expr, // No mapping, return as-is
50930            };
50931            return Expression::Var(Box::new(Var {
50932                this: mapped.to_string(),
50933            }));
50934        }
50935        expr
50936    }
50937
50938    fn try_parse_date_part_unit_expr(&self, expr: &Expression) -> Option<IntervalUnit> {
50939        let upper = self.date_part_expr_name(expr)?.to_ascii_uppercase();
50940        let canonical = match upper.as_str() {
50941            // Year
50942            "Y" | "YY" | "YYY" | "YYYY" | "YR" | "YEARS" | "YRS" => "YEAR",
50943            // Quarter
50944            "Q" | "QTR" | "QTRS" | "QUARTERS" | "QQ" => "QUARTER",
50945            // Month
50946            "MM" | "MON" | "MONS" | "MONTHS" | "M" => "MONTH",
50947            // Week
50948            "W" | "WK" | "WEEKOFYEAR" | "WOY" | "WY" | "WW" | "WEEKS" => "WEEK",
50949            // Day
50950            "D" | "DD" | "DAYS" | "DAYOFMONTH" => "DAY",
50951            // Hour
50952            "H" | "HH" | "HR" | "HOURS" | "HRS" => "HOUR",
50953            // Minute
50954            "MI" | "MIN" | "MINUTES" | "MINS" | "N" => "MINUTE",
50955            // Second
50956            "S" | "SEC" | "SECONDS" | "SECS" | "SS" => "SECOND",
50957            // Millisecond
50958            "MS" | "MSEC" | "MSECS" | "MSECOND" | "MSECONDS" | "MILLISEC" | "MILLISECS"
50959            | "MILLISECON" | "MILLISECONDS" => "MILLISECOND",
50960            // Microsecond
50961            "US" | "USEC" | "USECS" | "MICROSEC" | "MICROSECS" | "USECOND" | "USECONDS"
50962            | "MICROSECONDS" | "MCS" => "MICROSECOND",
50963            // Nanosecond
50964            "NS" | "NSEC" | "NANOSEC" | "NSECOND" | "NSECONDS" | "NANOSECS" => "NANOSECOND",
50965            _ => upper.as_str(),
50966        };
50967
50968        Self::parse_interval_unit_from_string(canonical)
50969    }
50970
50971    fn try_parse_date_part_unit_identifier_expr(&self, expr: &Expression) -> Option<IntervalUnit> {
50972        let upper = self.date_part_identifier_expr_name(expr)?.to_ascii_uppercase();
50973        let canonical = match upper.as_str() {
50974            "Y" | "YY" | "YYY" | "YYYY" | "YR" | "YEARS" | "YRS" => "YEAR",
50975            "Q" | "QTR" | "QTRS" | "QUARTERS" | "QQ" => "QUARTER",
50976            "MM" | "MON" | "MONS" | "MONTHS" | "M" => "MONTH",
50977            "W" | "WK" | "WEEKOFYEAR" | "WOY" | "WY" | "WW" | "WEEKS" => "WEEK",
50978            "D" | "DD" | "DAYS" | "DAYOFMONTH" => "DAY",
50979            "H" | "HH" | "HR" | "HOURS" | "HRS" => "HOUR",
50980            "MI" | "MIN" | "MINUTES" | "MINS" | "N" => "MINUTE",
50981            "S" | "SEC" | "SECONDS" | "SECS" | "SS" => "SECOND",
50982            "MS" | "MSEC" | "MSECS" | "MSECOND" | "MSECONDS" | "MILLISEC" | "MILLISECS"
50983            | "MILLISECON" | "MILLISECONDS" => "MILLISECOND",
50984            "US" | "USEC" | "USECS" | "MICROSEC" | "MICROSECS" | "USECOND" | "USECONDS"
50985            | "MICROSECONDS" | "MCS" => "MICROSECOND",
50986            "NS" | "NSEC" | "NANOSEC" | "NSECOND" | "NSECONDS" | "NANOSECS" => "NANOSECOND",
50987            _ => upper.as_str(),
50988        };
50989
50990        Self::parse_interval_unit_from_string(canonical)
50991    }
50992
50993    fn try_parse_date_part_field_identifier_expr(
50994        &self,
50995        expr: &Expression,
50996    ) -> Option<DateTimeField> {
50997        let upper = self.date_part_identifier_expr_name(expr)?.to_ascii_uppercase();
50998        Some(match upper.as_str() {
50999            "YEAR" | "Y" | "YY" | "YYY" | "YYYY" | "YR" | "YEARS" | "YRS" => DateTimeField::Year,
51000            "MONTH" | "MM" | "MON" | "MONS" | "MONTHS" => DateTimeField::Month,
51001            "DAY" | "D" | "DD" | "DAYS" | "DAYOFMONTH" => DateTimeField::Day,
51002            "HOUR" | "H" | "HH" | "HR" | "HOURS" | "HRS" => DateTimeField::Hour,
51003            "MINUTE" | "MI" | "MIN" | "MINUTES" | "MINS" => DateTimeField::Minute,
51004            "SECOND" | "S" | "SEC" | "SECONDS" | "SECS" => DateTimeField::Second,
51005            "MILLISECOND" | "MS" | "MSEC" | "MILLISECONDS" => DateTimeField::Millisecond,
51006            "MICROSECOND" | "US" | "USEC" | "MICROSECONDS" => DateTimeField::Microsecond,
51007            "DOW" | "DAYOFWEEK" | "DW" => DateTimeField::DayOfWeek,
51008            "DOY" | "DAYOFYEAR" | "DY" => DateTimeField::DayOfYear,
51009            "WEEK" | "W" | "WK" | "WEEKOFYEAR" | "WOY" | "WW" => DateTimeField::Week,
51010            "QUARTER" | "Q" | "QTR" | "QTRS" | "QUARTERS" => DateTimeField::Quarter,
51011            "EPOCH" | "EPOCH_SECOND" | "EPOCH_SECONDS" => DateTimeField::Epoch,
51012            "TIMEZONE" => DateTimeField::Timezone,
51013            "TIMEZONE_HOUR" | "TZH" => DateTimeField::TimezoneHour,
51014            "TIMEZONE_MINUTE" | "TZM" => DateTimeField::TimezoneMinute,
51015            "DATE" => DateTimeField::Date,
51016            "TIME" => DateTimeField::Time,
51017            other => DateTimeField::Custom(other.to_string()),
51018        })
51019    }
51020
51021    fn convert_date_part_identifier_expr_to_var(&self, expr: Expression) -> Expression {
51022        match expr {
51023            Expression::Var(_) => expr,
51024            Expression::Column(c) if c.table.is_none() => {
51025                Expression::Var(Box::new(Var { this: c.name.name }))
51026            }
51027            Expression::Identifier(id) => Expression::Var(Box::new(Var { this: id.name })),
51028            _ => expr,
51029        }
51030    }
51031
51032    fn date_part_identifier_expr_name<'a>(&self, expr: &'a Expression) -> Option<&'a str> {
51033        match expr {
51034            Expression::Var(v) => Some(v.this.as_str()),
51035            Expression::Column(c) if c.table.is_none() => Some(c.name.name.as_str()),
51036            Expression::Identifier(id) => Some(id.name.as_str()),
51037            _ => None,
51038        }
51039    }
51040
51041    fn date_part_expr_name<'a>(&self, expr: &'a Expression) -> Option<&'a str> {
51042        self.date_part_identifier_expr_name(expr).or(match expr {
51043            Expression::Literal(Literal::String(s)) => Some(s.as_str()),
51044            _ => None,
51045        })
51046    }
51047
51048    fn try_clickhouse_func_arg_alias(&mut self, expr: Expression) -> Expression {
51049        if !matches!(
51050            self.config.dialect,
51051            Some(crate::dialects::DialectType::ClickHouse)
51052        ) {
51053            return expr;
51054        }
51055        // Try implicit alias first
51056        if self.check(TokenType::Var) || self.check(TokenType::Identifier) {
51057            let next_after = self.peek_nth(1).map(|t| t.token_type);
51058            let is_delimiter = matches!(
51059                next_after,
51060                Some(TokenType::Comma)
51061                    | Some(TokenType::RParen)
51062                    | Some(TokenType::From)
51063                    | Some(TokenType::For)
51064                    | Some(TokenType::As)
51065            );
51066            if is_delimiter {
51067                let alias_token = self.advance();
51068                let alias_name = alias_token.text.clone();
51069                return Expression::Alias(Box::new(crate::expressions::Alias::new(
51070                    expr,
51071                    Identifier::new(alias_name),
51072                )));
51073            }
51074        }
51075        // Try explicit AS alias
51076        if self.check(TokenType::As) {
51077            let next_idx = self.current + 1;
51078            let after_alias_idx = self.current + 2;
51079            let is_alias_token = next_idx < self.tokens.len()
51080                && matches!(
51081                    self.tokens[next_idx].token_type,
51082                    TokenType::Identifier | TokenType::Var | TokenType::QuotedIdentifier
51083                );
51084            let is_delimiter = is_alias_token
51085                && after_alias_idx < self.tokens.len()
51086                && matches!(
51087                    self.tokens[after_alias_idx].token_type,
51088                    TokenType::Comma
51089                        | TokenType::RParen
51090                        | TokenType::From
51091                        | TokenType::For
51092                        | TokenType::As
51093                );
51094            if is_delimiter {
51095                self.skip(); // consume AS
51096                let alias_token = self.advance();
51097                let alias_name = if alias_token.token_type == TokenType::QuotedIdentifier {
51098                    let mut ident = Identifier::new(alias_token.text.clone());
51099                    ident.quoted = true;
51100                    ident
51101                } else {
51102                    Identifier::new(alias_token.text.clone())
51103                };
51104                return Expression::Alias(Box::new(crate::expressions::Alias::new(
51105                    expr, alias_name,
51106                )));
51107            }
51108        }
51109        expr
51110    }
51111
51112    /// parse_clickhouse_engine_expression - Parse ENGINE expression with optional args
51113    fn parse_clickhouse_engine_expression(&mut self) -> Result<Expression> {
51114        if self.is_at_end() {
51115            return Err(self.parse_error("Expected engine name after ENGINE"));
51116        }
51117
51118        let token = self.advance();
51119        let quoted = matches!(token.token_type, TokenType::QuotedIdentifier);
51120        let name = token.text.clone();
51121
51122        let ident = Expression::Identifier(Identifier {
51123            name,
51124            quoted,
51125            trailing_comments: Vec::new(),
51126            span: None,
51127        });
51128
51129        if self.match_token(TokenType::LParen) {
51130            let args = if self.check(TokenType::RParen) {
51131                Vec::new()
51132            } else {
51133                self.parse_expression_list()?
51134            };
51135            self.expect(TokenType::RParen)?;
51136            Ok(Expression::Anonymous(Box::new(Anonymous {
51137                this: Box::new(ident),
51138                expressions: args,
51139            })))
51140        } else {
51141            Ok(ident)
51142        }
51143    }
51144
51145    /// parse_property_assignment - Ported from Python _parse_property_assignment
51146    /// Parses a property assignment: optionally = or AS, then a value
51147    #[allow(unused_variables, unused_mut)]
51148    pub fn parse_property_assignment(&mut self) -> Result<Option<Expression>> {
51149        // Optionally match = or AS
51150        let _ = self.match_token(TokenType::Eq);
51151        let _ = self.match_token(TokenType::Alias);
51152
51153        // Parse the value as an unquoted field
51154        let value = self.parse_unquoted_field()?;
51155
51156        Ok(value)
51157    }
51158
51159    /// parse_property_before - Implemented from Python _parse_property_before
51160    #[allow(unused_variables, unused_mut)]
51161    pub fn parse_property_before(&mut self) -> Result<Option<Expression>> {
51162        if self.match_text_seq(&["NO"]) {
51163            // Matched: NO
51164            return Ok(None);
51165        }
51166        if self.match_text_seq(&["DUAL"]) {
51167            // Matched: DUAL
51168            return Ok(None);
51169        }
51170        if self.match_text_seq(&["BEFORE"]) {
51171            // Matched: BEFORE
51172            return Ok(None);
51173        }
51174        if self.match_texts(&["MIN", "MINIMUM"]) {
51175            // Matched one of: MIN, MINIMUM
51176            return Ok(None);
51177        }
51178        if self.match_texts(&["MAX", "MAXIMUM"]) {
51179            // Matched one of: MAX, MAXIMUM
51180            return Ok(None);
51181        }
51182        Ok(None)
51183    }
51184
51185    /// parse_qualify - Parse QUALIFY clause (Snowflake, BigQuery)
51186    /// Python: if not self._match(TokenType.QUALIFY): return None; return exp.Qualify(this=self._parse_disjunction())
51187    pub fn parse_qualify(&mut self) -> Result<Option<Expression>> {
51188        if !self.match_token(TokenType::Qualify) {
51189            return Ok(None);
51190        }
51191        let condition = self.parse_expression()?;
51192        Ok(Some(Expression::Qualify(Box::new(Qualify {
51193            this: condition,
51194        }))))
51195    }
51196
51197    /// parse_range - Parses range expressions (BETWEEN, LIKE, IN, IS, etc.)
51198    /// Python: _parse_range
51199    pub fn parse_range(&mut self) -> Result<Option<Expression>> {
51200        // First parse a bitwise expression as the left side
51201        let mut this = self.parse_bitwise()?;
51202        if this.is_none() {
51203            return Ok(None);
51204        }
51205
51206        // Check for NOT (for NOT LIKE, NOT IN, NOT BETWEEN, etc.)
51207        let negate = self.match_token(TokenType::Not);
51208
51209        // BETWEEN
51210        if self.match_token(TokenType::Between) {
51211            let between = self.parse_between_with_expr(this.clone(), negate)?;
51212            this = Some(between);
51213            return Ok(this);
51214        }
51215
51216        // LIKE
51217        if self.match_token(TokenType::Like) {
51218            let left = this.clone().expect("left expression checked above");
51219            let right = self
51220                .parse_bitwise()?
51221                .ok_or_else(|| self.parse_error("Expected expression after LIKE"))?;
51222            let escape = self.parse_escape()?;
51223            let like = Expression::Like(Box::new(LikeOp {
51224                left,
51225                right,
51226                escape,
51227                quantifier: None,
51228                inferred_type: None,
51229            }));
51230            this = if negate {
51231                Some(Expression::Not(Box::new(UnaryOp {
51232                    this: like,
51233                    inferred_type: None,
51234                })))
51235            } else {
51236                Some(like)
51237            };
51238            return Ok(this);
51239        }
51240
51241        // ILIKE
51242        if self.match_token(TokenType::ILike) {
51243            let left = this.clone().expect("left expression checked above");
51244            let right = self
51245                .parse_bitwise()?
51246                .ok_or_else(|| self.parse_error("Expected expression after ILIKE"))?;
51247            let escape = self.parse_escape()?;
51248            let ilike = Expression::ILike(Box::new(LikeOp {
51249                left,
51250                right,
51251                escape,
51252                quantifier: None,
51253                inferred_type: None,
51254            }));
51255            this = if negate {
51256                Some(Expression::Not(Box::new(UnaryOp {
51257                    this: ilike,
51258                    inferred_type: None,
51259                })))
51260            } else {
51261                Some(ilike)
51262            };
51263            return Ok(this);
51264        }
51265
51266        // IN
51267        if self.match_token(TokenType::In) {
51268            let in_expr = self.parse_in_with_expr(this.clone())?;
51269            this = if negate {
51270                Some(Expression::Not(Box::new(UnaryOp {
51271                    this: in_expr,
51272                    inferred_type: None,
51273                })))
51274            } else {
51275                Some(in_expr)
51276            };
51277            return Ok(this);
51278        }
51279
51280        // IS [NOT] NULL / IS [NOT] TRUE / IS [NOT] FALSE
51281        if self.match_token(TokenType::Is) {
51282            let is_expr = self.parse_is_with_expr(this.clone())?;
51283            this = Some(is_expr);
51284            return Ok(this);
51285        }
51286
51287        // Handle standalone NOT with NULL (for NOT NULL pattern after negate)
51288        if negate && self.match_token(TokenType::Null) {
51289            if let Some(left) = this {
51290                let is_null = Expression::Is(Box::new(BinaryOp {
51291                    left,
51292                    right: Expression::Null(Null),
51293                    left_comments: Vec::new(),
51294                    operator_comments: Vec::new(),
51295                    trailing_comments: Vec::new(),
51296                    inferred_type: None,
51297                }));
51298                return Ok(Some(Expression::Not(Box::new(UnaryOp {
51299                    this: is_null,
51300                    inferred_type: None,
51301                }))));
51302            }
51303        }
51304
51305        Ok(this)
51306    }
51307
51308    /// parse_between_with_expr - Parses BETWEEN expression with given left side
51309    fn parse_between_with_expr(
51310        &mut self,
51311        this: Option<Expression>,
51312        negate: bool,
51313    ) -> Result<Expression> {
51314        let this_expr = match this {
51315            Some(e) => e,
51316            None => return Err(self.parse_error("Expected expression before BETWEEN")),
51317        };
51318
51319        // Check for SYMMETRIC/ASYMMETRIC qualifier
51320        let symmetric = if self.match_texts(&["SYMMETRIC"]) {
51321            Some(true)
51322        } else if self.match_texts(&["ASYMMETRIC"]) {
51323            Some(false)
51324        } else {
51325            None
51326        };
51327
51328        let low = self
51329            .parse_bitwise()?
51330            .ok_or_else(|| self.parse_error("Expected low expression after BETWEEN"))?;
51331
51332        if !self.match_token(TokenType::And) {
51333            return Err(self.parse_error("Expected AND in BETWEEN expression"));
51334        }
51335
51336        let high = self
51337            .parse_bitwise()?
51338            .ok_or_else(|| self.parse_error("Expected high expression after AND in BETWEEN"))?;
51339
51340        Ok(Expression::Between(Box::new(Between {
51341            this: this_expr,
51342            low,
51343            high,
51344            not: negate,
51345            symmetric,
51346        })))
51347    }
51348
51349    /// parse_in_with_expr - Parses IN expression with given left side
51350    fn parse_in_with_expr(&mut self, this: Option<Expression>) -> Result<Expression> {
51351        let this_expr = match this {
51352            Some(e) => e,
51353            None => return Err(self.parse_error("Expected expression before IN")),
51354        };
51355
51356        // BigQuery: IN UNNEST(expr) — UNNEST without wrapping parentheses
51357        if self.check_identifier("UNNEST") {
51358            self.skip(); // consume UNNEST
51359            self.expect(TokenType::LParen)?;
51360            let unnest_expr = self.parse_expression()?;
51361            self.expect(TokenType::RParen)?;
51362            return Ok(Expression::In(Box::new(In {
51363                this: this_expr,
51364                expressions: Vec::new(),
51365                query: None,
51366                not: false,
51367                global: false,
51368                unnest: Some(Box::new(unnest_expr)),
51369                is_field: false,
51370            })));
51371        }
51372
51373        // Parse the IN list (subquery or value list)
51374        if !self.match_token(TokenType::LParen) {
51375            // DuckDB: IN without parentheses for array/list membership: 'red' IN tbl.flags
51376            // Try to parse as a single expression (column/array reference)
51377            if let Ok(expr) = self.parse_primary() {
51378                return Ok(Expression::In(Box::new(In {
51379                    this: this_expr,
51380                    expressions: vec![expr],
51381                    query: None,
51382                    not: false,
51383                    global: false,
51384                    unnest: None,
51385                    is_field: true,
51386                })));
51387            }
51388            return Err(self.parse_error("Expected expression or parenthesized list after IN"));
51389        }
51390
51391        // Check if it's a subquery
51392        if self.check(TokenType::Select) {
51393            let subquery = self.parse_select()?;
51394            self.expect(TokenType::RParen)?;
51395            return Ok(Expression::In(Box::new(In {
51396                this: this_expr,
51397                expressions: Vec::new(),
51398                query: Some(subquery),
51399                not: false,
51400                global: false,
51401                unnest: None,
51402                is_field: false,
51403            })));
51404        }
51405
51406        // Parse value list. Pre-size for large IN lists to reduce reallocations.
51407        let capacity_hint = self.estimate_expression_list_capacity_until_rparen();
51408        let expressions = self.parse_expression_list_with_capacity(capacity_hint)?;
51409        self.expect(TokenType::RParen)?;
51410
51411        if expressions.is_empty() {
51412            return Err(self.parse_error("Expected expression list after IN"));
51413        }
51414
51415        Ok(Expression::In(Box::new(In {
51416            this: this_expr,
51417            expressions,
51418            query: None,
51419            not: false,
51420            global: false,
51421            unnest: None,
51422            is_field: false,
51423        })))
51424    }
51425
51426    /// parse_is_with_expr - Parses IS expression with given left side
51427    fn parse_is_with_expr(&mut self, this: Option<Expression>) -> Result<Expression> {
51428        let this_expr = match this {
51429            Some(e) => e,
51430            None => return Err(self.parse_error("Expected expression before IS")),
51431        };
51432
51433        let negate = self.match_token(TokenType::Not);
51434
51435        // IS NULL
51436        if self.match_token(TokenType::Null) {
51437            let is_null = Expression::Is(Box::new(BinaryOp {
51438                left: this_expr,
51439                right: Expression::Null(Null),
51440                left_comments: Vec::new(),
51441                operator_comments: Vec::new(),
51442                trailing_comments: Vec::new(),
51443                inferred_type: None,
51444            }));
51445            return if negate {
51446                Ok(Expression::Not(Box::new(UnaryOp {
51447                    this: is_null,
51448                    inferred_type: None,
51449                })))
51450            } else {
51451                Ok(is_null)
51452            };
51453        }
51454
51455        // IS TRUE
51456        if self.match_texts(&["TRUE"]) {
51457            let is_true = Expression::Is(Box::new(BinaryOp {
51458                left: this_expr,
51459                right: Expression::Boolean(BooleanLiteral { value: true }),
51460                left_comments: Vec::new(),
51461                operator_comments: Vec::new(),
51462                trailing_comments: Vec::new(),
51463                inferred_type: None,
51464            }));
51465            return if negate {
51466                Ok(Expression::Not(Box::new(UnaryOp {
51467                    this: is_true,
51468                    inferred_type: None,
51469                })))
51470            } else {
51471                Ok(is_true)
51472            };
51473        }
51474
51475        // IS FALSE
51476        if self.match_texts(&["FALSE"]) {
51477            let is_false = Expression::Is(Box::new(BinaryOp {
51478                left: this_expr,
51479                right: Expression::Boolean(BooleanLiteral { value: false }),
51480                left_comments: Vec::new(),
51481                operator_comments: Vec::new(),
51482                trailing_comments: Vec::new(),
51483                inferred_type: None,
51484            }));
51485            return if negate {
51486                Ok(Expression::Not(Box::new(UnaryOp {
51487                    this: is_false,
51488                    inferred_type: None,
51489                })))
51490            } else {
51491                Ok(is_false)
51492            };
51493        }
51494
51495        // IS JSON [VALUE|SCALAR|OBJECT|ARRAY] [WITH UNIQUE KEYS|WITHOUT UNIQUE KEYS|UNIQUE KEYS]
51496        if self.match_texts(&["JSON"]) {
51497            // Parse optional JSON type
51498            let json_type = if self.match_texts(&["VALUE"]) {
51499                Some("VALUE".to_string())
51500            } else if self.match_texts(&["SCALAR"]) {
51501                Some("SCALAR".to_string())
51502            } else if self.match_texts(&["OBJECT"]) {
51503                Some("OBJECT".to_string())
51504            } else if self.match_texts(&["ARRAY"]) {
51505                Some("ARRAY".to_string())
51506            } else {
51507                None
51508            };
51509
51510            // Parse optional key uniqueness constraint
51511            let unique_keys = if self.match_text_seq(&["WITH", "UNIQUE", "KEYS"]) {
51512                Some(JsonUniqueKeys::With)
51513            } else if self.match_text_seq(&["WITHOUT", "UNIQUE", "KEYS"]) {
51514                Some(JsonUniqueKeys::Without)
51515            } else if self.match_text_seq(&["UNIQUE", "KEYS"]) {
51516                // Shorthand for WITH UNIQUE KEYS
51517                Some(JsonUniqueKeys::Shorthand)
51518            } else {
51519                None
51520            };
51521
51522            return Ok(Expression::IsJson(Box::new(IsJson {
51523                this: this_expr,
51524                json_type,
51525                unique_keys,
51526                negated: negate,
51527            })));
51528        }
51529
51530        // IS DISTINCT FROM / IS NOT DISTINCT FROM
51531        if self.match_text_seq(&["DISTINCT", "FROM"]) {
51532            let right = self.parse_bitwise()?;
51533            if let Some(right_expr) = right {
51534                // IS DISTINCT FROM is semantically "not equal with null handling"
51535                // Use NullSafeNeq for IS DISTINCT FROM
51536                // If negate was set (IS NOT DISTINCT FROM), use NullSafeEq
51537                let expr = if negate {
51538                    Expression::NullSafeEq(Box::new(BinaryOp {
51539                        left: this_expr,
51540                        right: right_expr,
51541                        left_comments: Vec::new(),
51542                        operator_comments: Vec::new(),
51543                        trailing_comments: Vec::new(),
51544                        inferred_type: None,
51545                    }))
51546                } else {
51547                    Expression::NullSafeNeq(Box::new(BinaryOp {
51548                        left: this_expr,
51549                        right: right_expr,
51550                        left_comments: Vec::new(),
51551                        operator_comments: Vec::new(),
51552                        trailing_comments: Vec::new(),
51553                        inferred_type: None,
51554                    }))
51555                };
51556                return Ok(expr);
51557            }
51558            return Err(self.parse_error("Expected expression after IS DISTINCT FROM"));
51559        }
51560
51561        Err(self.parse_error("Expected NULL, TRUE, FALSE, JSON, or DISTINCT FROM after IS"))
51562    }
51563
51564    /// parse_reads_property - Implemented from Python _parse_reads_property
51565    #[allow(unused_variables, unused_mut)]
51566    pub fn parse_reads_property(&mut self) -> Result<Option<Expression>> {
51567        if self.match_text_seq(&["SQL", "DATA"]) {
51568            // Matched: SQL DATA
51569            return Ok(None);
51570        }
51571        Ok(None)
51572    }
51573
51574    /// parse_recursive_with_search - Parse SEARCH/CYCLE clause for recursive CTEs (PostgreSQL)
51575    /// Syntax: SEARCH BREADTH|DEPTH FIRST BY column SET column [USING column]
51576    ///     or: CYCLE column SET column USING column
51577    #[allow(unused_variables, unused_mut)]
51578    pub fn parse_recursive_with_search(&mut self) -> Result<Option<Box<Expression>>> {
51579        // Check for SEARCH or CYCLE keyword
51580        let kind = if self.match_text_seq(&["SEARCH"]) {
51581            // SEARCH BREADTH|DEPTH FIRST BY ...
51582            let search_kind = if self.match_text_seq(&["BREADTH"]) {
51583                "BREADTH"
51584            } else if self.match_text_seq(&["DEPTH"]) {
51585                "DEPTH"
51586            } else {
51587                return Ok(None);
51588            };
51589            // Consume "FIRST BY"
51590            self.match_text_seq(&["FIRST"]);
51591            self.match_text_seq(&["BY"]);
51592            search_kind.to_string()
51593        } else if self.match_token(TokenType::Cycle) {
51594            "CYCLE".to_string()
51595        } else {
51596            return Ok(None);
51597        };
51598
51599        // Parse the column(s) - for CYCLE this is typically a single column
51600        let this = self.expect_identifier()?;
51601        let this_expr = Expression::Identifier(Identifier::new(this));
51602
51603        // SET column
51604        let expression = if self.match_text_seq(&["SET"]) {
51605            let set_col = self.expect_identifier()?;
51606            Expression::Identifier(Identifier::new(set_col))
51607        } else {
51608            return Err(self.parse_error("Expected SET in CYCLE/SEARCH clause"));
51609        };
51610
51611        // USING column (optional for SEARCH, required for CYCLE)
51612        let using = if self.match_token(TokenType::Using) {
51613            let using_col = self.expect_identifier()?;
51614            Some(Box::new(Expression::Identifier(Identifier::new(using_col))))
51615        } else {
51616            None
51617        };
51618
51619        Ok(Some(Box::new(Expression::RecursiveWithSearch(Box::new(
51620            RecursiveWithSearch {
51621                kind,
51622                this: Box::new(this_expr),
51623                expression: Box::new(expression),
51624                using,
51625            },
51626        )))))
51627    }
51628
51629    /// parse_references - Ported from Python _parse_references
51630    /// Parses REFERENCES clause for foreign key constraints
51631    #[allow(unused_variables, unused_mut)]
51632    pub fn parse_references(&mut self) -> Result<Option<Expression>> {
51633        if !self.match_token(TokenType::References) {
51634            return Ok(None);
51635        }
51636
51637        // Parse referenced table
51638        let this = self.parse_table()?;
51639        if this.is_none() {
51640            return Err(self.parse_error("Expected table name after REFERENCES"));
51641        }
51642
51643        // Parse optional column list (table(col1, col2))
51644        let expressions = if self.match_token(TokenType::LParen) {
51645            let cols = self.parse_identifier_list()?;
51646            self.expect(TokenType::RParen)?;
51647            cols.into_iter()
51648                .map(|id| Expression::Identifier(id))
51649                .collect()
51650        } else {
51651            Vec::new()
51652        };
51653
51654        // Parse optional constraint options (ON DELETE, ON UPDATE, etc.)
51655        let options = self.parse_fk_constraint_options()?;
51656
51657        Ok(Some(Expression::Reference(Box::new(Reference {
51658            this: Box::new(this.unwrap()),
51659            expressions,
51660            options,
51661        }))))
51662    }
51663
51664    /// Parse key constraint options (ON DELETE CASCADE, ON UPDATE SET NULL, etc.)
51665    fn parse_fk_constraint_options(&mut self) -> Result<Vec<Expression>> {
51666        let mut options = Vec::new();
51667
51668        while self.match_token(TokenType::On) {
51669            let kind = if self.match_token(TokenType::Delete) {
51670                "DELETE"
51671            } else if self.match_token(TokenType::Update) {
51672                "UPDATE"
51673            } else {
51674                break;
51675            };
51676
51677            let action = if self.match_text_seq(&["NO", "ACTION"]) {
51678                "NO ACTION"
51679            } else if self.match_text_seq(&["SET", "NULL"]) {
51680                "SET NULL"
51681            } else if self.match_text_seq(&["SET", "DEFAULT"]) {
51682                "SET DEFAULT"
51683            } else if self.match_token(TokenType::Cascade) {
51684                "CASCADE"
51685            } else if self.match_token(TokenType::Restrict) {
51686                "RESTRICT"
51687            } else {
51688                continue;
51689            };
51690
51691            // Store as simple identifier with the full action description
51692            options.push(Expression::Identifier(Identifier {
51693                name: format!("ON {} {}", kind, action),
51694                quoted: false,
51695                trailing_comments: Vec::new(),
51696                span: None,
51697            }));
51698        }
51699
51700        // Parse MATCH option
51701        if self.match_token(TokenType::Match) {
51702            let match_type = if self.match_identifier("FULL") {
51703                "FULL"
51704            } else if self.match_identifier("PARTIAL") {
51705                "PARTIAL"
51706            } else if self.match_identifier("SIMPLE") {
51707                "SIMPLE"
51708            } else {
51709                ""
51710            };
51711            if !match_type.is_empty() {
51712                options.push(Expression::Identifier(Identifier {
51713                    name: format!("MATCH {}", match_type),
51714                    quoted: false,
51715                    trailing_comments: Vec::new(),
51716                    span: None,
51717                }));
51718            }
51719        }
51720
51721        Ok(options)
51722    }
51723
51724    /// parse_refresh - Implemented from Python _parse_refresh
51725    #[allow(unused_variables, unused_mut)]
51726    /// parse_refresh - Parses REFRESH TABLE or REFRESH MATERIALIZED VIEW
51727    /// Python: parser.py:7656-7668
51728    pub fn parse_refresh(&mut self) -> Result<Option<Expression>> {
51729        let kind = if self.match_token(TokenType::Table) {
51730            "TABLE".to_string()
51731        } else if self.match_text_seq(&["MATERIALIZED", "VIEW"]) {
51732            "MATERIALIZED VIEW".to_string()
51733        } else {
51734            String::new()
51735        };
51736
51737        // Parse the object name (string literal or table name)
51738        // First try a string literal, then fall back to table reference
51739        if let Some(s) = self.parse_string()? {
51740            return Ok(Some(Expression::Refresh(Box::new(Refresh {
51741                this: Box::new(s),
51742                kind,
51743            }))));
51744        }
51745
51746        // Parse as a table reference (schema.table format)
51747        let table_ref = self.parse_table_ref()?;
51748        let table_expr = Expression::Table(Box::new(table_ref));
51749
51750        Ok(Some(Expression::Refresh(Box::new(Refresh {
51751            this: Box::new(table_expr),
51752            kind,
51753        }))))
51754    }
51755
51756    /// parse_refresh_trigger_property - Doris REFRESH clause for materialized views
51757    /// Syntax: REFRESH method ON kind [EVERY n UNIT] [STARTS 'datetime']
51758    /// Examples:
51759    ///   REFRESH COMPLETE ON MANUAL
51760    ///   REFRESH AUTO ON COMMIT
51761    ///   REFRESH AUTO ON SCHEDULE EVERY 5 MINUTE STARTS '2025-01-01 00:00:00'
51762    pub fn parse_refresh_trigger_property(&mut self) -> Result<RefreshTriggerProperty> {
51763        // Parse method: COMPLETE or AUTO
51764        let method = self.expect_identifier_or_keyword()?.to_ascii_uppercase();
51765
51766        // Parse ON
51767        self.expect(TokenType::On)?;
51768
51769        // Parse kind: MANUAL, COMMIT, or SCHEDULE
51770        let kind_text = self.expect_identifier_or_keyword()?.to_ascii_uppercase();
51771        let kind = Some(kind_text.clone());
51772
51773        // For SCHEDULE, parse EVERY n UNIT [STARTS 'datetime']
51774        let (every, unit, starts) = if kind_text == "SCHEDULE" {
51775            // EVERY n UNIT
51776            let every = if self.match_identifier("EVERY") {
51777                // parse_number returns Option<Expression> with Expression::Literal(Literal::Number(...))
51778                self.parse_number()?.map(Box::new)
51779            } else {
51780                None
51781            };
51782
51783            // Unit: MINUTE, HOUR, DAY, etc.
51784            let unit = if every.is_some() {
51785                Some(self.expect_identifier_or_keyword()?.to_ascii_uppercase())
51786            } else {
51787                None
51788            };
51789
51790            // STARTS 'datetime'
51791            let starts = if self.match_identifier("STARTS") {
51792                let s = self.expect_string()?;
51793                Some(Box::new(Expression::Literal(Literal::String(s))))
51794            } else {
51795                None
51796            };
51797
51798            (every, unit, starts)
51799        } else {
51800            (None, None, None)
51801        };
51802
51803        Ok(RefreshTriggerProperty {
51804            method,
51805            kind,
51806            every,
51807            unit,
51808            starts,
51809        })
51810    }
51811
51812    /// parse_remote_with_connection - Implemented from Python _parse_remote_with_connection
51813    #[allow(unused_variables, unused_mut)]
51814    pub fn parse_remote_with_connection(&mut self) -> Result<Option<Expression>> {
51815        if self.match_text_seq(&["WITH", "CONNECTION"]) {
51816            // Matched: WITH CONNECTION
51817            return Ok(None);
51818        }
51819        Ok(None)
51820    }
51821
51822    /// parse_respect_or_ignore_nulls - Implemented from Python _parse_respect_or_ignore_nulls
51823    #[allow(unused_variables, unused_mut)]
51824    pub fn parse_respect_or_ignore_nulls(&mut self) -> Result<Option<Expression>> {
51825        if self.match_text_seq(&["IGNORE", "NULLS"]) {
51826            // Matched: IGNORE NULLS
51827            return Ok(None);
51828        }
51829        if self.match_text_seq(&["RESPECT", "NULLS"]) {
51830            // Matched: RESPECT NULLS
51831            return Ok(None);
51832        }
51833        Ok(None)
51834    }
51835
51836    /// parse_retention_period - Parses HISTORY_RETENTION_PERIOD (TSQL)
51837    /// Python: _parse_retention_period
51838    /// Format: INFINITE | <number> DAY | DAYS | MONTH | MONTHS | YEAR | YEARS
51839    pub fn parse_retention_period(&mut self) -> Result<Option<Expression>> {
51840        // Try to parse a number first
51841        let number = self.parse_number()?;
51842        let number_str = number
51843            .map(|n| match n {
51844                Expression::Literal(Literal::Number(s)) => format!("{} ", s),
51845                _ => String::new(),
51846            })
51847            .unwrap_or_default();
51848
51849        // Parse the unit (any token as a variable)
51850        let unit = self.parse_var_any_token()?;
51851        let unit_str = unit
51852            .map(|u| match u {
51853                Expression::Var(v) => v.this.clone(),
51854                _ => String::new(),
51855            })
51856            .unwrap_or_default();
51857
51858        let result = format!("{}{}", number_str, unit_str);
51859        Ok(Some(Expression::Var(Box::new(Var { this: result }))))
51860    }
51861
51862    /// parse_var_any_token - Parses any token as a Var (for flexible parsing)
51863    fn parse_var_any_token(&mut self) -> Result<Option<Expression>> {
51864        if !self.is_at_end() {
51865            let token = self.advance();
51866            Ok(Some(Expression::Var(Box::new(Var {
51867                this: token.text.clone(),
51868            }))))
51869        } else {
51870            Ok(None)
51871        }
51872    }
51873
51874    /// parse_returning - Creates Returning expression
51875    /// Parses RETURNING clause (PostgreSQL) for INSERT/UPDATE/DELETE
51876    #[allow(unused_variables, unused_mut)]
51877    pub fn parse_returning(&mut self) -> Result<Option<Expression>> {
51878        if !self.match_token(TokenType::Returning) {
51879            return Ok(None);
51880        }
51881
51882        // Parse expressions (column list or *)
51883        let expressions = self.parse_expression_list()?;
51884
51885        // Check for INTO target_table (Oracle style)
51886        let into = if self.match_token(TokenType::Into) {
51887            self.parse_table()?.map(Box::new)
51888        } else {
51889            None
51890        };
51891
51892        Ok(Some(Expression::Returning(Box::new(Returning {
51893            expressions,
51894            into,
51895        }))))
51896    }
51897
51898    /// parse_output_clause - Parses OUTPUT clause (TSQL)
51899    /// Used in INSERT/UPDATE/DELETE and MERGE statements
51900    /// Supports expressions with optional AS aliases: OUTPUT col1, col2 AS alias, col3
51901    pub fn parse_output_clause(&mut self) -> Result<OutputClause> {
51902        // Parse comma-separated list of columns/expressions with optional aliases
51903        let mut columns = Vec::new();
51904        loop {
51905            let expr = self.parse_expression()?;
51906            // Check for optional AS alias
51907            let expr = if self.match_token(TokenType::As) {
51908                let alias = self.expect_identifier_or_keyword_with_quoted()?;
51909                Expression::Alias(Box::new(Alias {
51910                    this: expr,
51911                    alias,
51912                    column_aliases: Vec::new(),
51913                    pre_alias_comments: Vec::new(),
51914                    trailing_comments: Vec::new(),
51915                    inferred_type: None,
51916                }))
51917            } else {
51918                expr
51919            };
51920            columns.push(expr);
51921            if !self.match_token(TokenType::Comma) {
51922                break;
51923            }
51924        }
51925
51926        // Check for INTO target
51927        let into_table = if self.match_token(TokenType::Into) {
51928            Some(self.parse_expression()?)
51929        } else {
51930            None
51931        };
51932
51933        Ok(OutputClause {
51934            columns,
51935            into_table,
51936        })
51937    }
51938
51939    /// parse_returns - Implemented from Python _parse_returns
51940    /// Calls: parse_types
51941    #[allow(unused_variables, unused_mut)]
51942    pub fn parse_returns(&mut self) -> Result<Option<Expression>> {
51943        if self.match_text_seq(&["NULL", "ON", "NULL", "INPUT"]) {
51944            return Ok(Some(Expression::Schema(Box::new(Schema {
51945                this: None,
51946                expressions: Vec::new(),
51947            }))));
51948        }
51949        Ok(None)
51950    }
51951
51952    /// parse_row - Parses ROW FORMAT clause
51953    /// Returns RowFormatSerdeProperty or RowFormatDelimitedProperty
51954    pub fn parse_row(&mut self) -> Result<Option<Expression>> {
51955        // Python: if not self._match(TokenType.FORMAT): return None
51956        if !self.match_token(TokenType::Format) {
51957            return Ok(None);
51958        }
51959        self.parse_row_format()
51960    }
51961
51962    /// parse_row_format - Implemented from Python _parse_row_format
51963    /// Parses SERDE or DELIMITED row format specifications
51964    pub fn parse_row_format(&mut self) -> Result<Option<Expression>> {
51965        // Check for SERDE row format
51966        if self.match_text_seq(&["SERDE"]) {
51967            let this = self.parse_string()?;
51968            let serde_properties = self.parse_serde_properties(false)?;
51969
51970            return Ok(Some(Expression::RowFormatSerdeProperty(Box::new(
51971                RowFormatSerdeProperty {
51972                    this: Box::new(this.unwrap_or(Expression::Null(Null))),
51973                    serde_properties: serde_properties.map(Box::new),
51974                },
51975            ))));
51976        }
51977
51978        // Check for DELIMITED row format
51979        self.match_text_seq(&["DELIMITED"]);
51980
51981        let mut fields = None;
51982        let mut escaped = None;
51983        let mut collection_items = None;
51984        let mut map_keys = None;
51985        let mut lines = None;
51986        let mut null = None;
51987
51988        // Parse FIELDS TERMINATED BY
51989        if self.match_text_seq(&["FIELDS", "TERMINATED", "BY"]) {
51990            fields = self.parse_string()?.map(Box::new);
51991            // Parse optional ESCAPED BY
51992            if self.match_text_seq(&["ESCAPED", "BY"]) {
51993                escaped = self.parse_string()?.map(Box::new);
51994            }
51995        }
51996
51997        // Parse COLLECTION ITEMS TERMINATED BY
51998        if self.match_text_seq(&["COLLECTION", "ITEMS", "TERMINATED", "BY"]) {
51999            collection_items = self.parse_string()?.map(Box::new);
52000        }
52001
52002        // Parse MAP KEYS TERMINATED BY
52003        if self.match_text_seq(&["MAP", "KEYS", "TERMINATED", "BY"]) {
52004            map_keys = self.parse_string()?.map(Box::new);
52005        }
52006
52007        // Parse LINES TERMINATED BY
52008        if self.match_text_seq(&["LINES", "TERMINATED", "BY"]) {
52009            lines = self.parse_string()?.map(Box::new);
52010        }
52011
52012        // Parse NULL DEFINED AS
52013        if self.match_text_seq(&["NULL", "DEFINED", "AS"]) {
52014            null = self.parse_string()?.map(Box::new);
52015        }
52016
52017        // Parse optional WITH SERDEPROPERTIES
52018        let serde = self.parse_serde_properties(false)?.map(Box::new);
52019
52020        Ok(Some(Expression::RowFormatDelimitedProperty(Box::new(
52021            RowFormatDelimitedProperty {
52022                fields,
52023                escaped,
52024                collection_items,
52025                map_keys,
52026                lines,
52027                null,
52028                serde,
52029            },
52030        ))))
52031    }
52032
52033    /// parse_schema - Ported from Python _parse_schema
52034    /// Parses schema definition: (col1 type1, col2 type2, ...)
52035    /// Used for CREATE TABLE column definitions
52036    #[allow(unused_variables, unused_mut)]
52037    pub fn parse_schema(&mut self) -> Result<Option<Expression>> {
52038        self.parse_schema_with_this(None)
52039    }
52040
52041    /// parse_schema_with_this - Parses schema with optional table reference
52042    fn parse_schema_with_this(&mut self, this: Option<Expression>) -> Result<Option<Expression>> {
52043        // Check for opening parenthesis
52044        if !self.match_token(TokenType::LParen) {
52045            return Ok(this.map(|e| e));
52046        }
52047
52048        // Check if this is a subquery (SELECT, WITH, etc.) not a schema
52049        if self.check(TokenType::Select) || self.check(TokenType::With) {
52050            // Retreat - put back the LParen
52051            self.current -= 1;
52052            return Ok(this.map(|e| e));
52053        }
52054
52055        // Parse column definitions and constraints
52056        let mut expressions = Vec::new();
52057        if !self.check(TokenType::RParen) {
52058            loop {
52059                // Try to parse constraint first, then field definition
52060                if let Some(constraint) = self.parse_constraint()? {
52061                    expressions.push(constraint);
52062                } else if let Some(field_def) = self.parse_field_def()? {
52063                    expressions.push(field_def);
52064                } else {
52065                    break;
52066                }
52067
52068                if !self.match_token(TokenType::Comma) {
52069                    break;
52070                }
52071            }
52072        }
52073
52074        self.expect(TokenType::RParen)?;
52075
52076        Ok(Some(Expression::Schema(Box::new(Schema {
52077            this: this.map(Box::new),
52078            expressions,
52079        }))))
52080    }
52081
52082    /// Parse schema identifier: name or name(columns)
52083    /// Used for TSQL ON filegroup (partition_column) syntax
52084    fn parse_schema_identifier(&mut self) -> Result<Expression> {
52085        // Parse the identifier (filegroup name)
52086        let name = self.expect_identifier_with_quoted()?;
52087        let name_expr = Expression::Identifier(name);
52088
52089        // Check for optional parenthesized columns
52090        if self.match_token(TokenType::LParen) {
52091            let mut columns = Vec::new();
52092            loop {
52093                let col = self.expect_identifier_with_quoted()?;
52094                columns.push(Expression::Identifier(col));
52095                if !self.match_token(TokenType::Comma) {
52096                    break;
52097                }
52098            }
52099            self.expect(TokenType::RParen)?;
52100            Ok(Expression::Schema(Box::new(Schema {
52101                this: Some(Box::new(name_expr)),
52102                expressions: columns,
52103            })))
52104        } else {
52105            // Just the identifier, no columns
52106            Ok(name_expr)
52107        }
52108    }
52109
52110    /// parse_security - Implemented from Python _parse_security
52111    #[allow(unused_variables, unused_mut)]
52112    pub fn parse_security(&mut self) -> Result<Option<Expression>> {
52113        if self.match_texts(&["NONE", "DEFINER", "INVOKER"]) {
52114            // Matched one of: NONE, DEFINER, INVOKER
52115            return Ok(None);
52116        }
52117        Ok(None)
52118    }
52119
52120    /// parse_select_or_expression - Parses either a SELECT statement or an expression
52121    /// Python: _parse_select_or_expression
52122    pub fn parse_select_or_expression(&mut self) -> Result<Option<Expression>> {
52123        // Save position for potential backtracking
52124        let start_pos = self.current;
52125
52126        // First try to parse a SELECT statement if we're at a SELECT keyword
52127        if self.check(TokenType::Select) {
52128            return Ok(Some(self.parse_select()?));
52129        }
52130
52131        // Otherwise try to parse an expression (assignment)
52132        if let Some(expr) = self.parse_disjunction()? {
52133            return Ok(Some(expr));
52134        }
52135
52136        // Backtrack if nothing worked
52137        self.current = start_pos;
52138
52139        Ok(None)
52140    }
52141
52142    /// parse_select_query - Implemented from Python _parse_select_query
52143    /// Calls: parse_string, parse_table, parse_describe
52144    #[allow(unused_variables, unused_mut)]
52145    pub fn parse_select_query(&mut self) -> Result<Option<Expression>> {
52146        if self.match_texts(&["STRUCT", "VALUE"]) {
52147            // Matched one of: STRUCT, VALUE
52148            return Ok(None);
52149        }
52150        Ok(None)
52151    }
52152
52153    /// parse_sequence_properties - Implemented from Python _parse_sequence_properties
52154    /// Calls: parse_number, parse_term, parse_column
52155    #[allow(unused_variables, unused_mut)]
52156    pub fn parse_sequence_properties(&mut self) -> Result<Option<Expression>> {
52157        if self.match_text_seq(&["INCREMENT"]) {
52158            return Ok(Some(Expression::SequenceProperties(Box::new(
52159                SequenceProperties {
52160                    increment: None,
52161                    minvalue: None,
52162                    maxvalue: None,
52163                    cache: None,
52164                    start: None,
52165                    owned: None,
52166                    options: Vec::new(),
52167                },
52168            ))));
52169        }
52170        if self.match_text_seq(&["BY"]) {
52171            // Matched: BY
52172            return Ok(None);
52173        }
52174        if self.match_text_seq(&["="]) {
52175            // Matched: =
52176            return Ok(None);
52177        }
52178        Ok(None)
52179    }
52180
52181    /// parse_serde_properties - Implemented from Python _parse_serde_properties
52182    /// Parses SERDEPROPERTIES clause: [WITH] SERDEPROPERTIES (key=value, ...)
52183    pub fn parse_serde_properties(&mut self, with_: bool) -> Result<Option<Expression>> {
52184        let start_index = self.current;
52185        let has_with = with_ || self.match_text_seq(&["WITH"]);
52186
52187        // Check for SERDEPROPERTIES keyword
52188        if !self.match_token(TokenType::SerdeProperties) {
52189            self.current = start_index;
52190            return Ok(None);
52191        }
52192
52193        // Parse wrapped properties manually since parse_property doesn't handle 'key'='value' syntax
52194        let mut expressions = Vec::new();
52195        if self.match_token(TokenType::LParen) {
52196            loop {
52197                if self.check(TokenType::RParen) {
52198                    break;
52199                }
52200                // Parse 'key'='value' or key=value
52201                let key = self.parse_primary()?;
52202                if self.match_token(TokenType::Eq) {
52203                    let value = self.parse_primary()?;
52204                    expressions.push(Expression::Eq(Box::new(BinaryOp::new(key, value))));
52205                } else {
52206                    expressions.push(key);
52207                }
52208                if !self.match_token(TokenType::Comma) {
52209                    break;
52210                }
52211            }
52212            self.expect(TokenType::RParen)?;
52213        }
52214
52215        Ok(Some(Expression::SerdeProperties(Box::new(
52216            SerdeProperties {
52217                expressions,
52218                with_: if has_with {
52219                    Some(Box::new(Expression::Boolean(BooleanLiteral {
52220                        value: true,
52221                    })))
52222                } else {
52223                    None
52224                },
52225            },
52226        ))))
52227    }
52228
52229    /// parse_session_parameter - Ported from Python _parse_session_parameter
52230    #[allow(unused_variables, unused_mut)]
52231    /// parse_session_parameter - Parses session parameters (@@var or @@session.var)
52232    /// Example: @@session.sql_mode, @@global.autocommit
52233    pub fn parse_session_parameter(&mut self) -> Result<Option<Expression>> {
52234        // Parse the first identifier or primary
52235        let first = if let Some(id) = self.parse_id_var()? {
52236            id
52237        } else if let Some(primary) = self.parse_primary_or_var()? {
52238            primary
52239        } else {
52240            return Ok(None);
52241        };
52242
52243        // Check for dot notation (kind.name)
52244        let (kind, this) = if self.match_token(TokenType::Dot) {
52245            // kind is the first part, parse the second
52246            let kind_name = match &first {
52247                Expression::Identifier(id) => Some(id.name.clone()),
52248                _ => None,
52249            };
52250            let second = self
52251                .parse_var()?
52252                .or_else(|| self.parse_primary_or_var().ok().flatten());
52253            (kind_name, second.unwrap_or(first))
52254        } else {
52255            (None, first)
52256        };
52257
52258        Ok(Some(Expression::SessionParameter(Box::new(
52259            SessionParameter {
52260                this: Box::new(this),
52261                kind,
52262            },
52263        ))))
52264    }
52265
52266    /// parse_set_item - Ported from Python _parse_set_item
52267    /// Parses an item in a SET statement (GLOBAL, LOCAL, SESSION prefixes, or assignment)
52268    #[allow(unused_variables, unused_mut)]
52269    pub fn parse_set_item(&mut self) -> Result<Option<Expression>> {
52270        // Check for specific prefixes
52271        let kind = if self.match_text_seq(&["GLOBAL"]) {
52272            Some("GLOBAL".to_string())
52273        } else if self.match_text_seq(&["LOCAL"]) {
52274            Some("LOCAL".to_string())
52275        } else if self.match_text_seq(&["SESSION"]) {
52276            Some("SESSION".to_string())
52277        } else {
52278            None
52279        };
52280
52281        // Delegate to set_item_assignment
52282        self.parse_set_item_assignment()
52283    }
52284
52285    /// parse_set_item_assignment - Implemented from Python _parse_set_item_assignment
52286    /// Parses SET variable = value assignments
52287    pub fn parse_set_item_assignment(&mut self) -> Result<Option<Expression>> {
52288        let start_index = self.current;
52289
52290        // Try to parse as TRANSACTION
52291        if self.match_text_seq(&["TRANSACTION"]) {
52292            // This is handled by parse_set_transaction
52293            return Ok(Some(Expression::SetItem(Box::new(SetItem {
52294                name: Expression::Var(Box::new(Var {
52295                    this: "TRANSACTION".to_string(),
52296                })),
52297                value: Expression::Null(Null),
52298                kind: None,
52299                no_equals: false,
52300            }))));
52301        }
52302
52303        // Parse left side: primary or column
52304        let left = self
52305            .parse_primary_or_var()?
52306            .or_else(|| self.parse_column().ok().flatten());
52307
52308        if left.is_none() {
52309            self.current = start_index;
52310            return Ok(None);
52311        }
52312
52313        // Check for assignment delimiter (= or TO or :=)
52314        if !self.match_texts(&["=", "TO", ":="]) {
52315            self.current = start_index;
52316            return Ok(None);
52317        }
52318
52319        // Parse right side: value
52320        // First try string literals (preserve quoting), then booleans/numbers, then identifiers
52321        let right_val = if self.check(TokenType::String) {
52322            let text = self.advance().text.clone();
52323            Expression::Literal(Literal::String(text))
52324        } else if self.check(TokenType::False) {
52325            self.skip();
52326            Expression::Boolean(BooleanLiteral { value: false })
52327        } else if self.check(TokenType::True) {
52328            self.skip();
52329            Expression::Boolean(BooleanLiteral { value: true })
52330        } else {
52331            let right = self
52332                .parse_id_var()?
52333                .or_else(|| self.parse_primary_or_var().ok().flatten());
52334            // Convert Column/Identifier to Var
52335            match right {
52336                Some(Expression::Column(col)) => Expression::Var(Box::new(Var {
52337                    this: col.name.name.clone(),
52338                })),
52339                Some(Expression::Identifier(id)) => Expression::Var(Box::new(Var {
52340                    this: id.name.clone(),
52341                })),
52342                Some(other) => other,
52343                None => Expression::Null(Null),
52344            }
52345        };
52346
52347        Ok(Some(Expression::SetItem(Box::new(SetItem {
52348            name: left
52349                .ok_or_else(|| self.parse_error("Expected variable name in SET statement"))?,
52350            value: right_val,
52351            kind: None,
52352            no_equals: false,
52353        }))))
52354    }
52355
52356    /// parse_set_operations - Parses UNION/INTERSECT/EXCEPT operations
52357    /// This version parses from current position (expects to be at set operator)
52358    /// Python: _parse_set_operations
52359    pub fn parse_set_operations(&mut self) -> Result<Option<Expression>> {
52360        // Parse a SELECT or subquery first
52361        let left = if self.check(TokenType::Select) {
52362            Some(self.parse_select()?)
52363        } else if self.match_token(TokenType::LParen) {
52364            let inner = self.parse_select()?;
52365            self.match_token(TokenType::RParen);
52366            Some(inner)
52367        } else {
52368            None
52369        };
52370
52371        if left.is_none() {
52372            return Ok(None);
52373        }
52374
52375        self.parse_set_operations_with_expr(left)
52376    }
52377
52378    /// parse_set_operations_with_expr - Parses set operations with a left expression
52379    pub fn parse_set_operations_with_expr(
52380        &mut self,
52381        this: Option<Expression>,
52382    ) -> Result<Option<Expression>> {
52383        let mut result = this;
52384
52385        while result.is_some() {
52386            if let Some(setop) = self.parse_set_operation_with_expr(result.clone())? {
52387                result = Some(setop);
52388            } else {
52389                break;
52390            }
52391        }
52392
52393        Ok(result)
52394    }
52395
52396    /// parse_set_operation_with_expr - Parses a single set operation (UNION, INTERSECT, EXCEPT)
52397    fn parse_set_operation_with_expr(
52398        &mut self,
52399        left: Option<Expression>,
52400    ) -> Result<Option<Expression>> {
52401        let left_expr = match left {
52402            Some(e) => e,
52403            None => return Ok(None),
52404        };
52405
52406        // Check for UNION, INTERSECT, EXCEPT
52407        let op_type = if self.match_token(TokenType::Union) {
52408            "UNION"
52409        } else if self.match_token(TokenType::Intersect) {
52410            "INTERSECT"
52411        } else if self.match_token(TokenType::Except) {
52412            "EXCEPT"
52413        } else {
52414            return Ok(Some(left_expr));
52415        };
52416
52417        // Check for ALL or DISTINCT
52418        let (all, distinct) = if self.match_token(TokenType::All) {
52419            (true, false)
52420        } else {
52421            let d = self.match_token(TokenType::Distinct);
52422            (false, d)
52423        };
52424
52425        // DuckDB: UNION [ALL] BY NAME SELECT ...
52426        let by_name = self.match_token(TokenType::By) && self.match_identifier("NAME");
52427
52428        // Parse the right side (SELECT or subquery)
52429        let right = if self.check(TokenType::Select) {
52430            self.parse_select()?
52431        } else if self.match_token(TokenType::LParen) {
52432            let inner = self.parse_select()?;
52433            self.match_token(TokenType::RParen);
52434            inner
52435        } else {
52436            return Ok(Some(left_expr));
52437        };
52438
52439        // Create the appropriate set operation expression
52440        match op_type {
52441            "UNION" => Ok(Some(Expression::Union(Box::new(Union {
52442                left: left_expr,
52443                right,
52444                all,
52445                distinct,
52446                with: None,
52447                order_by: None,
52448                limit: None,
52449                offset: None,
52450                distribute_by: None,
52451                sort_by: None,
52452                cluster_by: None,
52453                by_name,
52454                side: None,
52455                kind: None,
52456                corresponding: false,
52457                strict: false,
52458                on_columns: Vec::new(),
52459            })))),
52460            "INTERSECT" => Ok(Some(Expression::Intersect(Box::new(Intersect {
52461                left: left_expr,
52462                right,
52463                all,
52464                distinct,
52465                with: None,
52466                order_by: None,
52467                limit: None,
52468                offset: None,
52469                distribute_by: None,
52470                sort_by: None,
52471                cluster_by: None,
52472                by_name,
52473                side: None,
52474                kind: None,
52475                corresponding: false,
52476                strict: false,
52477                on_columns: Vec::new(),
52478            })))),
52479            "EXCEPT" => Ok(Some(Expression::Except(Box::new(Except {
52480                left: left_expr,
52481                right,
52482                all,
52483                distinct,
52484                with: None,
52485                order_by: None,
52486                limit: None,
52487                offset: None,
52488                distribute_by: None,
52489                sort_by: None,
52490                cluster_by: None,
52491                by_name,
52492                side: None,
52493                kind: None,
52494                corresponding: false,
52495                strict: false,
52496                on_columns: Vec::new(),
52497            })))),
52498            _ => Ok(Some(left_expr)),
52499        }
52500    }
52501
52502    /// parse_set_transaction - Implemented from Python _parse_set_transaction
52503    #[allow(unused_variables, unused_mut)]
52504    pub fn parse_set_transaction(&mut self) -> Result<Option<Expression>> {
52505        if self.match_text_seq(&["TRANSACTION"]) {
52506            // Matched: TRANSACTION
52507            return Ok(None);
52508        }
52509        Ok(None)
52510    }
52511
52512    /// Helper to consume an optional ClickHouse SETTINGS clause
52513    /// Used in SHOW, CHECK TABLE, and other ClickHouse statements
52514    fn parse_clickhouse_settings_clause(&mut self) -> Result<()> {
52515        if self.match_token(TokenType::Settings) {
52516            let _ = self.parse_settings_property()?;
52517        }
52518        Ok(())
52519    }
52520
52521    /// parse_settings_property - Parses SETTINGS property (ClickHouse)
52522    /// Python: _parse_settings_property
52523    /// Format: SETTINGS key=value, key=value, ...
52524    pub fn parse_settings_property(&mut self) -> Result<Option<Expression>> {
52525        // Parse comma-separated assignment expressions
52526        let mut expressions = Vec::new();
52527        loop {
52528            if let Some(assignment) = self.parse_assignment()? {
52529                expressions.push(assignment);
52530            } else {
52531                break;
52532            }
52533            if !self.match_token(TokenType::Comma) {
52534                break;
52535            }
52536        }
52537
52538        Ok(Some(Expression::SettingsProperty(Box::new(
52539            SettingsProperty { expressions },
52540        ))))
52541    }
52542
52543    /// parse_simplified_pivot - Ported from Python _parse_simplified_pivot
52544    /// Handles DuckDB simplified PIVOT/UNPIVOT syntax:
52545    ///   PIVOT table ON columns [IN (...)] USING agg_func [AS alias], ... [GROUP BY ...]
52546    ///   UNPIVOT table ON columns [INTO NAME col VALUE col, ...]
52547    #[allow(unused_variables, unused_mut)]
52548    pub fn parse_simplified_pivot(&mut self, is_unpivot: bool) -> Result<Option<Expression>> {
52549        // Parse the source table (can be a subquery like (SELECT 1 AS col1, 2 AS col2))
52550        let this = if self.check(TokenType::LParen) {
52551            // Could be parenthesized subquery
52552            self.skip(); // consume (
52553            if self.check(TokenType::Select) || self.check(TokenType::With) {
52554                let inner = self.parse_statement()?;
52555                self.expect(TokenType::RParen)?;
52556                Some(Expression::Subquery(Box::new(Subquery {
52557                    this: inner,
52558                    alias: None,
52559                    column_aliases: Vec::new(),
52560                    order_by: None,
52561                    limit: None,
52562                    offset: None,
52563                    lateral: false,
52564                    modifiers_inside: false,
52565                    trailing_comments: Vec::new(),
52566                    distribute_by: None,
52567                    sort_by: None,
52568                    cluster_by: None,
52569                    inferred_type: None,
52570                })))
52571            } else {
52572                // Not a subquery, retreat and parse as expression in parens
52573                self.current -= 1; // un-consume the (
52574                Some(self.parse_primary()?)
52575            }
52576        } else {
52577            // Parse table reference (e.g., Cities, schema.table, duckdb_functions())
52578            Some(self.parse_primary()?)
52579        };
52580
52581        // Parse ON columns
52582        let expressions = if self.match_text_seq(&["ON"]) {
52583            let mut on_exprs = Vec::new();
52584            loop {
52585                // Parse ON expression - use parse_bitwise to handle complex expressions like Country || '_' || Name
52586                let on_expr = self.parse_bitwise()?;
52587                if on_expr.is_none() {
52588                    break;
52589                }
52590                let mut expr = on_expr.unwrap();
52591
52592                // Check for IN clause on this column
52593                if self.match_token(TokenType::In) {
52594                    if self.match_token(TokenType::LParen) {
52595                        let mut in_exprs = Vec::new();
52596                        loop {
52597                            if self.check(TokenType::RParen) {
52598                                break;
52599                            }
52600                            if let Some(val) = self.parse_select_or_expression()? {
52601                                in_exprs.push(val);
52602                            }
52603                            if !self.match_token(TokenType::Comma) {
52604                                break;
52605                            }
52606                        }
52607                        self.expect(TokenType::RParen)?;
52608                        expr = Expression::In(Box::new(In {
52609                            this: expr,
52610                            expressions: in_exprs,
52611                            query: None,
52612                            not: false,
52613                            global: false,
52614                            unnest: None,
52615                            is_field: false,
52616                        }));
52617                    }
52618                }
52619                // Check for alias (UNPIVOT ON (jan, feb, mar) AS q1, ...)
52620                else if self.match_token(TokenType::As) {
52621                    let alias_name = self.expect_identifier()?;
52622                    expr =
52623                        Expression::Alias(Box::new(Alias::new(expr, Identifier::new(alias_name))));
52624                }
52625
52626                on_exprs.push(expr);
52627
52628                // Continue if comma
52629                if !self.match_token(TokenType::Comma) {
52630                    break;
52631                }
52632            }
52633            on_exprs
52634        } else {
52635            Vec::new()
52636        };
52637
52638        // Parse INTO for UNPIVOT columns (INTO NAME col VALUE col, ...)
52639        let into = self.parse_unpivot_columns()?;
52640
52641        // Parse USING clause (aggregation functions with optional aliases)
52642        // e.g., USING SUM(Population), USING SUM(Population) AS total, MAX(Population) AS max
52643        // e.g., USING CAST(AVG(LENGTH(function_name)) AS INT)
52644        let using = if self.match_text_seq(&["USING"]) {
52645            let mut using_exprs = Vec::new();
52646            loop {
52647                // Stop if we hit GROUP BY or end of input
52648                if self.is_at_end() || self.check(TokenType::Group) || self.check(TokenType::RParen)
52649                {
52650                    break;
52651                }
52652                // Parse the primary expression (function call, possibly with cast :: operator)
52653                let func = self.parse_primary()?;
52654                // Check for :: cast operator (e.g., SUM(Population)::INTEGER)
52655                let expr = if self.match_token(TokenType::DColon) {
52656                    let data_type = self.parse_data_type()?;
52657                    Expression::Cast(Box::new(Cast {
52658                        this: func,
52659                        to: data_type,
52660                        trailing_comments: Vec::new(),
52661                        double_colon_syntax: true,
52662                        format: None,
52663                        default: None,
52664                        inferred_type: None,
52665                    }))
52666                } else {
52667                    func
52668                };
52669                // Try to parse alias (AS alias)
52670                if self.match_token(TokenType::As) {
52671                    let alias_name = self.expect_identifier()?;
52672                    using_exprs.push(Expression::Alias(Box::new(Alias::new(
52673                        expr,
52674                        Identifier::new(alias_name),
52675                    ))));
52676                } else {
52677                    using_exprs.push(expr);
52678                }
52679                if !self.match_token(TokenType::Comma) {
52680                    break;
52681                }
52682            }
52683            using_exprs
52684        } else {
52685            Vec::new()
52686        };
52687
52688        // Parse optional GROUP BY
52689        let group = self.parse_group()?;
52690
52691        let source = this.unwrap();
52692
52693        Ok(Some(Expression::Pivot(Box::new(Pivot {
52694            this: source,
52695            expressions,
52696            fields: Vec::new(),
52697            using,
52698            group: group.map(Box::new),
52699            unpivot: is_unpivot,
52700            into: into.map(Box::new),
52701            alias: None,
52702            include_nulls: None,
52703            default_on_null: None,
52704            with: None,
52705        }))))
52706    }
52707
52708    /// parse_slice - Parses array slice syntax [start:end:step]
52709    /// Python: _parse_slice
52710    /// Takes an optional 'this' expression (the start of the slice)
52711    pub fn parse_slice(&mut self) -> Result<Option<Expression>> {
52712        self.parse_slice_with_this(None)
52713    }
52714
52715    /// Implementation of parse_slice with 'this' parameter
52716    pub fn parse_slice_with_this(
52717        &mut self,
52718        this: Option<Expression>,
52719    ) -> Result<Option<Expression>> {
52720        // Check for colon - if not found, return this as-is
52721        if !self.match_token(TokenType::Colon) {
52722            return Ok(this);
52723        }
52724
52725        // Parse end expression
52726        // Handle special case: -: which means -1 (from end)
52727        let end = if self.check(TokenType::Dash) && self.check_next(TokenType::Colon) {
52728            // -: pattern means -1 (from end)
52729            self.skip(); // consume dash
52730            Some(Expression::Neg(Box::new(UnaryOp::new(
52731                Expression::Literal(Literal::Number("1".to_string())),
52732            ))))
52733        } else if self.check(TokenType::Colon) || self.check(TokenType::RBracket) {
52734            // Empty end like [start::step] or [start:]
52735            None
52736        } else {
52737            Some(self.parse_unary()?)
52738        };
52739
52740        // Parse optional step expression after second colon
52741        let step = if self.match_token(TokenType::Colon) {
52742            if self.check(TokenType::RBracket) {
52743                None
52744            } else {
52745                Some(self.parse_unary()?)
52746            }
52747        } else {
52748            None
52749        };
52750
52751        Ok(Some(Expression::Slice(Box::new(Slice {
52752            this: this.map(Box::new),
52753            expression: end.map(Box::new),
52754            step: step.map(Box::new),
52755        }))))
52756    }
52757
52758    /// Parse a slice element (start, end, or step in array slicing)
52759    /// This uses parse_unary to avoid interpreting : as parameter syntax
52760    /// Returns None for empty elements (e.g., [:] or [::step])
52761    fn parse_slice_element(&mut self) -> Result<Option<Expression>> {
52762        // Check for empty element (next is : or ])
52763        if self.check(TokenType::Colon) || self.check(TokenType::RBracket) {
52764            return Ok(None);
52765        }
52766        // Handle special case: -: means -1 (from the end)
52767        // This is used in slicing like [:-:-1] where the first -: means end=-1
52768        if self.check(TokenType::Dash) && self.check_next(TokenType::Colon) {
52769            self.skip(); // consume dash
52770                            // Don't consume the colon - let the caller handle it
52771            return Ok(Some(Expression::Neg(Box::new(UnaryOp::new(
52772                Expression::Literal(Literal::Number("1".to_string())),
52773            )))));
52774        }
52775        // Parse full expression (including binary ops like y - 1) but stop at : or ]
52776        let expr = self.parse_disjunction()?;
52777        Ok(expr)
52778    }
52779
52780    /// parse_sort - Ported from Python _parse_sort
52781    /// Parses SORT BY clause (Hive/Spark)
52782    #[allow(unused_variables, unused_mut)]
52783    pub fn parse_sort(&mut self) -> Result<Option<Expression>> {
52784        // Check for SORT BY token
52785        if !self.match_keywords(&[TokenType::Sort, TokenType::By]) {
52786            return Ok(None);
52787        }
52788
52789        // Parse comma-separated ordered expressions
52790        let mut expressions = Vec::new();
52791        loop {
52792            if let Some(ordered) = self.parse_ordered_item()? {
52793                expressions.push(ordered);
52794            } else {
52795                break;
52796            }
52797            if !self.match_token(TokenType::Comma) {
52798                break;
52799            }
52800        }
52801
52802        Ok(Some(Expression::SortBy(Box::new(SortBy { expressions }))))
52803    }
52804
52805    /// parse_cluster_by_clause - Parses CLUSTER BY clause (Hive/Spark)
52806    #[allow(unused_variables, unused_mut)]
52807    pub fn parse_cluster_by_clause(&mut self) -> Result<Option<Expression>> {
52808        if !self.match_keywords(&[TokenType::Cluster, TokenType::By]) {
52809            return Ok(None);
52810        }
52811
52812        // Parse comma-separated ordered expressions
52813        let mut expressions: Vec<Ordered> = Vec::new();
52814        loop {
52815            if let Some(ordered) = self.parse_ordered_item()? {
52816                expressions.push(ordered);
52817            } else {
52818                break;
52819            }
52820            if !self.match_token(TokenType::Comma) {
52821                break;
52822            }
52823        }
52824        Ok(Some(Expression::ClusterBy(Box::new(ClusterBy {
52825            expressions,
52826        }))))
52827    }
52828
52829    /// parse_distribute_by_clause - Parses DISTRIBUTE BY clause (Hive/Spark)
52830    #[allow(unused_variables, unused_mut)]
52831    pub fn parse_distribute_by_clause(&mut self) -> Result<Option<Expression>> {
52832        if !self.match_keywords(&[TokenType::Distribute, TokenType::By]) {
52833            return Ok(None);
52834        }
52835
52836        let expressions = self.parse_expression_list()?;
52837        Ok(Some(Expression::DistributeBy(Box::new(DistributeBy {
52838            expressions,
52839        }))))
52840    }
52841
52842    /// parse_sortkey - Redshift/PostgreSQL SORTKEY property
52843    /// Parses SORTKEY(column1, column2, ...) with optional COMPOUND modifier
52844    #[allow(unused_variables, unused_mut)]
52845    pub fn parse_sortkey(&mut self) -> Result<Option<Expression>> {
52846        // Parse the wrapped list of columns/identifiers
52847        let this = if self.match_token(TokenType::LParen) {
52848            let mut columns = Vec::new();
52849            loop {
52850                if let Some(id) = self.parse_id_var()? {
52851                    columns.push(id);
52852                } else {
52853                    break;
52854                }
52855                if !self.match_token(TokenType::Comma) {
52856                    break;
52857                }
52858            }
52859            self.match_token(TokenType::RParen);
52860
52861            if columns.is_empty() {
52862                return Ok(None);
52863            }
52864
52865            if columns.len() == 1 {
52866                columns.into_iter().next().unwrap()
52867            } else {
52868                Expression::Tuple(Box::new(Tuple {
52869                    expressions: columns,
52870                }))
52871            }
52872        } else {
52873            // Single column without parens
52874            if let Some(id) = self.parse_id_var()? {
52875                id
52876            } else {
52877                return Ok(None);
52878            }
52879        };
52880
52881        Ok(Some(Expression::SortKeyProperty(Box::new(
52882            SortKeyProperty {
52883                this: Box::new(this),
52884                compound: None, // compound is set by caller if COMPOUND keyword was matched
52885            },
52886        ))))
52887    }
52888
52889    /// parse_star - Parse STAR (*) token with optional EXCEPT/REPLACE/RENAME
52890    /// Python: if self._match(TokenType.STAR): return self._parse_star_ops()
52891    pub fn parse_star(&mut self) -> Result<Option<Expression>> {
52892        if !self.match_token(TokenType::Star) {
52893            return Ok(None);
52894        }
52895
52896        // Parse optional EXCEPT/EXCLUDE columns
52897        let except = self.parse_star_except()?;
52898
52899        // Parse optional REPLACE expressions
52900        let replace = self.parse_star_replace()?;
52901
52902        // Parse optional RENAME columns
52903        let rename = self.parse_star_rename()?;
52904
52905        Ok(Some(Expression::Star(Star {
52906            table: None,
52907            except,
52908            replace,
52909            rename,
52910            trailing_comments: Vec::new(),
52911            span: None,
52912        })))
52913    }
52914
52915    /// try_parse_identifier - Try to parse an identifier, returning None if not found
52916    fn try_parse_identifier(&mut self) -> Option<Identifier> {
52917        if self.is_identifier_token() {
52918            let token = self.advance();
52919            let quoted = token.token_type == TokenType::QuotedIdentifier;
52920            Some(Identifier {
52921                name: token.text,
52922                quoted,
52923                trailing_comments: Vec::new(),
52924                span: None,
52925            })
52926        } else {
52927            None
52928        }
52929    }
52930
52931    /// parse_star_except - Parse EXCEPT/EXCLUDE clause for Star
52932    /// Example: * EXCEPT (col1, col2)
52933    fn parse_star_except(&mut self) -> Result<Option<Vec<Identifier>>> {
52934        if !self.match_texts(&["EXCEPT", "EXCLUDE"]) {
52935            return Ok(None);
52936        }
52937
52938        // Parse (col1, col2, ...)
52939        if self.match_token(TokenType::LParen) {
52940            let mut columns = Vec::new();
52941            loop {
52942                if let Some(id) = self.try_parse_identifier() {
52943                    columns.push(id);
52944                } else if self.is_safe_keyword_as_identifier() {
52945                    // ClickHouse: allow keywords like 'key' as column names in EXCEPT
52946                    let token = self.advance();
52947                    columns.push(Identifier {
52948                        name: token.text,
52949                        quoted: false,
52950                        trailing_comments: Vec::new(),
52951                        span: None,
52952                    });
52953                } else {
52954                    break;
52955                }
52956                if !self.match_token(TokenType::Comma) {
52957                    break;
52958                }
52959            }
52960            self.match_token(TokenType::RParen);
52961            return Ok(Some(columns));
52962        }
52963
52964        // Single column without parens
52965        if let Some(id) = self.try_parse_identifier() {
52966            return Ok(Some(vec![id]));
52967        }
52968
52969        Ok(None)
52970    }
52971
52972    /// parse_star_replace - Parse REPLACE clause for Star
52973    /// Example: * REPLACE (col1 AS alias1, col2 AS alias2)
52974    fn parse_star_replace(&mut self) -> Result<Option<Vec<Alias>>> {
52975        if !self.match_texts(&["REPLACE"]) {
52976            return Ok(None);
52977        }
52978
52979        if self.match_token(TokenType::LParen) {
52980            let mut aliases = Vec::new();
52981            loop {
52982                // Parse expression AS alias
52983                if let Some(expr) = self.parse_disjunction()? {
52984                    let alias_name = if self.match_token(TokenType::As) {
52985                        self.try_parse_identifier()
52986                    } else {
52987                        None
52988                    };
52989
52990                    aliases.push(Alias {
52991                        this: expr,
52992                        alias: alias_name.unwrap_or_else(|| Identifier::new("")),
52993                        column_aliases: Vec::new(),
52994                        pre_alias_comments: Vec::new(),
52995                        trailing_comments: Vec::new(),
52996                        inferred_type: None,
52997                    });
52998                } else {
52999                    break;
53000                }
53001                if !self.match_token(TokenType::Comma) {
53002                    break;
53003                }
53004            }
53005            self.match_token(TokenType::RParen);
53006            return Ok(Some(aliases));
53007        }
53008
53009        Ok(None)
53010    }
53011
53012    /// parse_star_rename - Parse RENAME clause for Star
53013    /// Example: * RENAME (old_col AS new_col, ...)
53014    fn parse_star_rename(&mut self) -> Result<Option<Vec<(Identifier, Identifier)>>> {
53015        if !self.match_texts(&["RENAME"]) {
53016            return Ok(None);
53017        }
53018
53019        if self.match_token(TokenType::LParen) {
53020            let mut renames = Vec::new();
53021            loop {
53022                // Parse old_name AS new_name
53023                if let Some(old_name) = self.try_parse_identifier() {
53024                    if self.match_token(TokenType::As) {
53025                        if let Some(new_name) = self.try_parse_identifier() {
53026                            renames.push((old_name, new_name));
53027                        }
53028                    }
53029                } else {
53030                    break;
53031                }
53032                if !self.match_token(TokenType::Comma) {
53033                    break;
53034                }
53035            }
53036            self.match_token(TokenType::RParen);
53037            return Ok(Some(renames));
53038        }
53039
53040        Ok(None)
53041    }
53042
53043    /// parse_star_op - Helper to parse EXCEPT/REPLACE/RENAME with keywords
53044    /// Returns list of expressions if keywords match
53045    pub fn parse_star_op(&mut self, keywords: &[&str]) -> Result<Option<Vec<Expression>>> {
53046        if !self.match_texts(keywords) {
53047            return Ok(None);
53048        }
53049
53050        // If followed by paren, parse wrapped CSV
53051        if self.match_token(TokenType::LParen) {
53052            let expressions = self.parse_expression_list()?;
53053            self.match_token(TokenType::RParen);
53054            return Ok(Some(expressions));
53055        }
53056
53057        // Otherwise parse single aliased expression
53058        if let Some(expr) = self.parse_disjunction()? {
53059            // Try to parse explicit alias
53060            let result = if self.match_token(TokenType::As) {
53061                if let Some(alias_name) = self.try_parse_identifier() {
53062                    Expression::Alias(Box::new(Alias {
53063                        this: expr,
53064                        alias: alias_name,
53065                        column_aliases: Vec::new(),
53066                        pre_alias_comments: Vec::new(),
53067                        trailing_comments: Vec::new(),
53068                        inferred_type: None,
53069                    }))
53070                } else {
53071                    expr
53072                }
53073            } else {
53074                expr
53075            };
53076            return Ok(Some(vec![result]));
53077        }
53078
53079        Ok(None)
53080    }
53081
53082    /// parse_star_ops - Implemented from Python _parse_star_ops
53083    /// Creates a Star expression with EXCEPT/REPLACE/RENAME clauses
53084    /// Also handles * COLUMNS(pattern) syntax for DuckDB column selection
53085    pub fn parse_star_ops(&mut self) -> Result<Option<Expression>> {
53086        // Handle * COLUMNS(pattern) function (DuckDB)
53087        // This parses patterns like: * COLUMNS(c ILIKE '%suffix')
53088        if self.match_text_seq(&["COLUMNS"]) && self.check(TokenType::LParen) {
53089            // Parse the COLUMNS function arguments
53090            self.expect(TokenType::LParen)?;
53091            let this = self.parse_expression()?;
53092            self.expect(TokenType::RParen)?;
53093
53094            // Return a Columns expression with unpack=true (since it came from * COLUMNS())
53095            return Ok(Some(Expression::Columns(Box::new(Columns {
53096                this: Box::new(this),
53097                unpack: Some(Box::new(Expression::Boolean(BooleanLiteral {
53098                    value: true,
53099                }))),
53100            }))));
53101        }
53102
53103        // Parse EXCEPT/EXCLUDE
53104        let except_exprs = self.parse_star_op(&["EXCEPT", "EXCLUDE"])?;
53105        let except = except_exprs.map(|exprs| {
53106            exprs
53107                .into_iter()
53108                .filter_map(|e| match e {
53109                    Expression::Identifier(id) => Some(id),
53110                    Expression::Column(col) => Some(col.name),
53111                    _ => None,
53112                })
53113                .collect()
53114        });
53115
53116        // Parse REPLACE
53117        let replace_exprs = self.parse_star_op(&["REPLACE"])?;
53118        let replace = replace_exprs.map(|exprs| {
53119            exprs
53120                .into_iter()
53121                .filter_map(|e| match e {
53122                    Expression::Alias(a) => Some(*a),
53123                    _ => None,
53124                })
53125                .collect()
53126        });
53127
53128        // Parse RENAME
53129        let _rename_exprs = self.parse_star_op(&["RENAME"])?;
53130        let rename: Option<Vec<(Identifier, Identifier)>> = None; // Complex to extract from expressions
53131
53132        Ok(Some(Expression::Star(Star {
53133            table: None,
53134            except,
53135            replace,
53136            rename,
53137            trailing_comments: Vec::new(),
53138            span: None,
53139        })))
53140    }
53141
53142    /// parse_stored - Implemented from Python _parse_stored
53143    #[allow(unused_variables, unused_mut)]
53144    pub fn parse_stored(&mut self) -> Result<Option<Expression>> {
53145        if self.match_text_seq(&["BY"]) {
53146            return Ok(Some(Expression::InputOutputFormat(Box::new(
53147                InputOutputFormat {
53148                    input_format: None,
53149                    output_format: None,
53150                },
53151            ))));
53152        }
53153        if self.match_text_seq(&["INPUTFORMAT"]) {
53154            // Matched: INPUTFORMAT
53155            return Ok(None);
53156        }
53157        Ok(None)
53158    }
53159
53160    /// parse_stream - Implemented from Python _parse_stream
53161    #[allow(unused_variables, unused_mut)]
53162    pub fn parse_stream(&mut self) -> Result<Option<Expression>> {
53163        if self.match_text_seq(&["STREAM"]) {
53164            // Matched: STREAM
53165            return Ok(None);
53166        }
53167        Ok(None)
53168    }
53169
53170    /// parse_string - Parse string literal
53171    /// Python: if self._match_set(self.STRING_PARSERS): return STRING_PARSERS[token_type](...)
53172    pub fn parse_string(&mut self) -> Result<Option<Expression>> {
53173        // Regular string literal
53174        if self.match_token(TokenType::String) {
53175            let text = self.previous().text.clone();
53176            return Ok(Some(Expression::Literal(Literal::String(text))));
53177        }
53178        // National string (N'...')
53179        if self.match_token(TokenType::NationalString) {
53180            let text = self.previous().text.clone();
53181            return Ok(Some(Expression::Literal(Literal::NationalString(text))));
53182        }
53183        // Raw string (r"..." or r'...')
53184        if self.match_token(TokenType::RawString) {
53185            let text = self.previous().text.clone();
53186            return Ok(Some(Expression::Literal(Literal::RawString(text))));
53187        }
53188        // Heredoc string
53189        if self.match_token(TokenType::HeredocString) {
53190            let text = self.previous().text.clone();
53191            return Ok(Some(Expression::Literal(Literal::String(text))));
53192        }
53193        // Hex string (X'...' or 0x...)
53194        if self.match_token(TokenType::HexString) {
53195            let text = self.previous().text.clone();
53196            return Ok(Some(Expression::Literal(Literal::HexString(text))));
53197        }
53198        // Bit string (B'...')
53199        if self.match_token(TokenType::BitString) {
53200            let text = self.previous().text.clone();
53201            return Ok(Some(Expression::Literal(Literal::BitString(text))));
53202        }
53203        // Byte string (b"..." - BigQuery style)
53204        if self.match_token(TokenType::ByteString) {
53205            let text = self.previous().text.clone();
53206            return Ok(Some(Expression::Literal(Literal::ByteString(text))));
53207        }
53208        Ok(None)
53209    }
53210
53211    /// parse_string_agg - Parses STRING_AGG function arguments
53212    /// Python: parser.py:6849-6899
53213    /// Handles DISTINCT, separator, ORDER BY, ON OVERFLOW, WITHIN GROUP
53214    #[allow(unused_variables, unused_mut)]
53215    pub fn parse_string_agg(&mut self) -> Result<Option<Expression>> {
53216        // Check for DISTINCT
53217        let distinct = self.match_token(TokenType::Distinct);
53218
53219        // Parse main expression
53220        let this = self.parse_disjunction()?;
53221        if this.is_none() {
53222            return Ok(None);
53223        }
53224
53225        // Parse optional separator
53226        let separator = if self.match_token(TokenType::Comma) {
53227            self.parse_disjunction()?
53228        } else {
53229            None
53230        };
53231
53232        // Parse ON OVERFLOW clause
53233        let on_overflow = if self.match_text_seq(&["ON", "OVERFLOW"]) {
53234            if self.match_text_seq(&["ERROR"]) {
53235                Some(Box::new(Expression::Var(Box::new(Var {
53236                    this: "ERROR".to_string(),
53237                }))))
53238            } else {
53239                self.match_text_seq(&["TRUNCATE"]);
53240                let truncate_str = self.parse_string()?;
53241                let with_count = if self.match_text_seq(&["WITH", "COUNT"]) {
53242                    Some(true)
53243                } else if self.match_text_seq(&["WITHOUT", "COUNT"]) {
53244                    Some(false)
53245                } else {
53246                    None
53247                };
53248                Some(Box::new(Expression::OverflowTruncateBehavior(Box::new(
53249                    OverflowTruncateBehavior {
53250                        this: truncate_str.map(Box::new),
53251                        with_count: with_count
53252                            .map(|c| Box::new(Expression::Boolean(BooleanLiteral { value: c }))),
53253                    },
53254                ))))
53255            }
53256        } else {
53257            None
53258        };
53259
53260        // Parse ORDER BY or WITHIN GROUP
53261        let order_by = if self.match_token(TokenType::OrderBy) {
53262            Some(self.parse_expression_list()?)
53263        } else if self.match_text_seq(&["WITHIN", "GROUP"]) {
53264            self.match_token(TokenType::LParen);
53265            let order = self.parse_order()?;
53266            self.match_token(TokenType::RParen);
53267            order.map(|o| vec![o])
53268        } else {
53269            None
53270        };
53271
53272        // Return as GroupConcat (which is the canonical form for STRING_AGG)
53273        Ok(Some(Expression::GroupConcat(Box::new(GroupConcatFunc {
53274            this: this.unwrap(),
53275            separator: separator,
53276            order_by: None,
53277            distinct,
53278            filter: None,
53279            inferred_type: None,
53280        }))))
53281    }
53282
53283    /// parse_string_as_identifier - Parses a string literal as a quoted identifier
53284    /// Python: _parse_string_as_identifier
53285    /// Used for cases where a string can be used as an identifier (e.g., MySQL)
53286    pub fn parse_string_as_identifier(&mut self) -> Result<Option<Expression>> {
53287        if self.match_token(TokenType::String) {
53288            let text = self.previous().text.clone();
53289            // Remove quotes if present
53290            let name = if text.starts_with('\'') && text.ends_with('\'') && text.len() >= 2 {
53291                text[1..text.len() - 1].to_string()
53292            } else if text.starts_with('"') && text.ends_with('"') && text.len() >= 2 {
53293                text[1..text.len() - 1].to_string()
53294            } else {
53295                text
53296            };
53297
53298            Ok(Some(Expression::Identifier(Identifier {
53299                name,
53300                quoted: true,
53301                trailing_comments: Vec::new(),
53302                span: None,
53303            })))
53304        } else {
53305            Ok(None)
53306        }
53307    }
53308
53309    /// parse_struct_types - Delegates to parse_types
53310    #[allow(unused_variables, unused_mut)]
53311    pub fn parse_struct_types(&mut self) -> Result<Option<Expression>> {
53312        self.parse_types()
53313    }
53314
53315    /// parse_subquery - Ported from Python _parse_subquery
53316    /// Parses a parenthesized SELECT as subquery: (SELECT ...)
53317    #[allow(unused_variables, unused_mut)]
53318    pub fn parse_subquery(&mut self) -> Result<Option<Expression>> {
53319        // Check for opening paren
53320        if !self.match_token(TokenType::LParen) {
53321            return Ok(None);
53322        }
53323
53324        // Check if it's a SELECT or WITH statement
53325        if !self.check(TokenType::Select) && !self.check(TokenType::With) {
53326            // Not a subquery, retreat
53327            self.current -= 1;
53328            return Ok(None);
53329        }
53330
53331        // Parse the query
53332        let query = self.parse_statement()?;
53333        self.expect(TokenType::RParen)?;
53334
53335        // Parse optional table alias
53336        let alias = self.parse_table_alias_if_present()?;
53337
53338        Ok(Some(Expression::Subquery(Box::new(Subquery {
53339            this: query,
53340            alias,
53341            column_aliases: Vec::new(),
53342            order_by: None,
53343            limit: None,
53344            offset: None,
53345            lateral: false,
53346            modifiers_inside: false,
53347            trailing_comments: Vec::new(),
53348            distribute_by: None,
53349            sort_by: None,
53350            cluster_by: None,
53351            inferred_type: None,
53352        }))))
53353    }
53354
53355    /// Helper to parse table alias if present
53356    fn parse_table_alias_if_present(&mut self) -> Result<Option<Identifier>> {
53357        // Check for AS keyword
53358        let explicit_as = self.match_token(TokenType::As);
53359
53360        // ClickHouse: keywords can be used as table aliases when AS is explicit
53361        let is_keyword_alias = explicit_as
53362            && matches!(
53363                self.config.dialect,
53364                Some(crate::dialects::DialectType::ClickHouse)
53365            )
53366            && self.peek().token_type.is_keyword();
53367
53368        // Try to parse identifier
53369        if self.check(TokenType::Identifier)
53370            || self.check(TokenType::QuotedIdentifier)
53371            || is_keyword_alias
53372        {
53373            if is_keyword_alias
53374                && !self.check(TokenType::Identifier)
53375                && !self.check(TokenType::QuotedIdentifier)
53376            {
53377                let token = self.advance();
53378                return Ok(Some(Identifier::new(token.text)));
53379            }
53380            if let Some(Expression::Identifier(id)) = self.parse_identifier()? {
53381                return Ok(Some(id));
53382            }
53383        } else if explicit_as {
53384            // AS was present but no identifier follows - this is an error
53385            return Err(self.parse_error("Expected identifier after AS"));
53386        }
53387
53388        Ok(None)
53389    }
53390
53391    /// parse_substring - Ported from Python _parse_substring
53392    /// Parses SUBSTRING function with two syntax variants:
53393    /// 1. Standard SQL: SUBSTRING(str FROM start [FOR length])
53394    /// 2. Function style: SUBSTRING(str, start, length)
53395    #[allow(unused_variables, unused_mut)]
53396    pub fn parse_substring(&mut self) -> Result<Option<Expression>> {
53397        // Parse initial comma-separated arguments
53398        let mut args: Vec<Expression> = Vec::new();
53399
53400        // Parse first argument (the string)
53401        match self.parse_bitwise() {
53402            Ok(Some(expr)) => {
53403                let expr = self.try_clickhouse_func_arg_alias(expr);
53404                args.push(expr);
53405            }
53406            Ok(None) => return Ok(None),
53407            Err(e) => return Err(e),
53408        }
53409
53410        // Check for comma-separated additional arguments
53411        while self.match_token(TokenType::Comma) {
53412            match self.parse_bitwise() {
53413                Ok(Some(expr)) => {
53414                    let expr = self.try_clickhouse_func_arg_alias(expr);
53415                    args.push(expr);
53416                }
53417                Ok(None) => break,
53418                Err(e) => return Err(e),
53419            }
53420        }
53421
53422        // Check for FROM/FOR syntax (SQL standard)
53423        let mut start: Option<Expression> = None;
53424        let mut length: Option<Expression> = None;
53425        let mut from_for_syntax = false;
53426
53427        loop {
53428            if self.match_token(TokenType::From) {
53429                from_for_syntax = true;
53430                match self.parse_bitwise() {
53431                    Ok(Some(expr)) => {
53432                        let expr = self.try_clickhouse_func_arg_alias(expr);
53433                        start = Some(expr);
53434                    }
53435                    Ok(None) => {}
53436                    Err(e) => return Err(e),
53437                }
53438            } else if self.match_token(TokenType::For) {
53439                from_for_syntax = true;
53440                // If no start specified yet, default to 1
53441                if start.is_none() {
53442                    start = Some(Expression::Literal(Literal::Number("1".to_string())));
53443                }
53444                match self.parse_bitwise() {
53445                    Ok(Some(expr)) => {
53446                        let expr = self.try_clickhouse_func_arg_alias(expr);
53447                        length = Some(expr);
53448                    }
53449                    Ok(None) => {}
53450                    Err(e) => return Err(e),
53451                }
53452            } else {
53453                break;
53454            }
53455        }
53456
53457        // Build the substring expression
53458        if args.is_empty() {
53459            return Ok(None);
53460        }
53461
53462        let this = args.remove(0);
53463
53464        // Determine start and length
53465        let final_start = if let Some(s) = start {
53466            s
53467        } else if !args.is_empty() {
53468            args.remove(0)
53469        } else {
53470            Expression::Literal(Literal::Number("1".to_string()))
53471        };
53472
53473        let final_length = if length.is_some() {
53474            length
53475        } else if !args.is_empty() {
53476            Some(args.remove(0))
53477        } else {
53478            None
53479        };
53480
53481        Ok(Some(Expression::Substring(Box::new(SubstringFunc {
53482            this,
53483            start: final_start,
53484            length: final_length,
53485            from_for_syntax,
53486        }))))
53487    }
53488
53489    /// parse_system_versioning_property - Implemented from Python _parse_system_versioning_property
53490    /// Calls: parse_table_parts, parse_retention_period
53491    #[allow(unused_variables, unused_mut)]
53492    pub fn parse_system_versioning_property(&mut self) -> Result<Option<Expression>> {
53493        if self.match_text_seq(&["OFF"]) {
53494            return Ok(Some(Expression::WithSystemVersioningProperty(Box::new(
53495                WithSystemVersioningProperty {
53496                    on: None,
53497                    this: None,
53498                    data_consistency: None,
53499                    retention_period: None,
53500                    with_: None,
53501                },
53502            ))));
53503        }
53504        if self.match_text_seq(&["HISTORY_TABLE", "="]) {
53505            // Matched: HISTORY_TABLE =
53506            return Ok(None);
53507        }
53508        if self.match_text_seq(&["DATA_CONSISTENCY_CHECK", "="]) {
53509            // Matched: DATA_CONSISTENCY_CHECK =
53510            return Ok(None);
53511        }
53512        Ok(None)
53513    }
53514
53515    /// Parse PostgreSQL ROWS FROM syntax:
53516    /// ROWS FROM (func1(args) AS alias1(col1 type1, col2 type2), func2(...) AS alias2(...)) [WITH ORDINALITY] [AS outer_alias(...)]
53517    fn parse_rows_from(&mut self) -> Result<Expression> {
53518        // Expect opening paren
53519        self.expect(TokenType::LParen)?;
53520
53521        let mut expressions = Vec::new();
53522
53523        loop {
53524            // Parse each function expression inside ROWS FROM
53525            // Each element is: func_name(args) [AS alias(col1 type1, col2 type2, ...)]
53526            let func_expr = self.parse_rows_from_function()?;
53527            expressions.push(func_expr);
53528
53529            if !self.match_token(TokenType::Comma) {
53530                break;
53531            }
53532        }
53533
53534        self.expect(TokenType::RParen)?;
53535
53536        // Check for WITH ORDINALITY
53537        let ordinality =
53538            if self.match_token(TokenType::With) && self.match_token(TokenType::Ordinality) {
53539                true
53540            } else {
53541                false
53542            };
53543
53544        // Check for outer alias: AS alias(col1 type1, col2 type2, ...)
53545        let alias = if self.match_token(TokenType::As) {
53546            Some(Box::new(self.parse_rows_from_alias()?))
53547        } else {
53548            None
53549        };
53550
53551        Ok(Expression::RowsFrom(Box::new(RowsFrom {
53552            expressions,
53553            ordinality,
53554            alias,
53555        })))
53556    }
53557
53558    /// Parse a single function in ROWS FROM: func_name(args) [AS alias(col1 type1, ...)]
53559    fn parse_rows_from_function(&mut self) -> Result<Expression> {
53560        // Parse function name
53561        let func_name = self.expect_identifier_or_keyword()?;
53562
53563        // Parse function arguments
53564        self.expect(TokenType::LParen)?;
53565        let args = if self.check(TokenType::RParen) {
53566            Vec::new()
53567        } else {
53568            self.parse_function_arguments()?
53569        };
53570        self.expect(TokenType::RParen)?;
53571
53572        let func_expr = Expression::Function(Box::new(Function {
53573            name: func_name,
53574            args,
53575            distinct: false,
53576            trailing_comments: Vec::new(),
53577            use_bracket_syntax: false,
53578            no_parens: false,
53579            quoted: false,
53580            span: None,
53581            inferred_type: None,
53582        }));
53583
53584        // Check for AS alias(col1 type1, col2 type2, ...)
53585        // Return a Tuple(function, TableAlias) so the generator can output: FUNC() AS alias(col type)
53586        if self.match_token(TokenType::As) {
53587            let alias_expr = self.parse_rows_from_alias()?;
53588            Ok(Expression::Tuple(Box::new(Tuple {
53589                expressions: vec![func_expr, alias_expr],
53590            })))
53591        } else {
53592            Ok(func_expr)
53593        }
53594    }
53595
53596    /// Parse ROWS FROM alias with typed columns: alias_name(col1 type1, col2 type2, ...)
53597    fn parse_rows_from_alias(&mut self) -> Result<Expression> {
53598        let alias_name = self.expect_identifier_or_keyword_with_quoted()?;
53599
53600        // Check for column definitions: (col1 type1, col2 type2, ...)
53601        let columns = if self.match_token(TokenType::LParen) {
53602            let mut cols = Vec::new();
53603            loop {
53604                if self.check(TokenType::RParen) {
53605                    break;
53606                }
53607                // Parse column name (can be quoted)
53608                let col_name = self.expect_identifier_or_keyword_with_quoted()?;
53609                // Parse column type
53610                let col_type = self.parse_data_type()?;
53611                // Create ColumnDef expression, preserving the quoted status
53612                let mut col_def = ColumnDef::new(col_name.name.clone(), col_type);
53613                col_def.name = col_name; // Preserve the full identifier with quoted flag
53614                cols.push(Expression::ColumnDef(Box::new(col_def)));
53615
53616                if !self.match_token(TokenType::Comma) {
53617                    break;
53618                }
53619            }
53620            self.expect(TokenType::RParen)?;
53621            cols
53622        } else {
53623            Vec::new()
53624        };
53625
53626        Ok(Expression::TableAlias(Box::new(TableAlias {
53627            this: Some(Box::new(Expression::Identifier(alias_name))),
53628            columns,
53629        })))
53630    }
53631
53632    /// parse_table - Implemented from Python _parse_table
53633    /// Calls: parse_table_hints, parse_unnest, parse_partition
53634    #[allow(unused_variables, unused_mut)]
53635    pub fn parse_table(&mut self) -> Result<Option<Expression>> {
53636        if self.match_text_seq(&["ROWS", "FROM"]) {
53637            // ROWS FROM is handled by parse_rows_from() in parse_table_expression()
53638            return Ok(None);
53639        }
53640        if self.match_text_seq(&["*"]) {
53641            // Matched: *
53642            return Ok(None);
53643        }
53644        if self.match_text_seq(&["NOT", "INDEXED"]) {
53645            // Matched: NOT INDEXED
53646            return Ok(None);
53647        }
53648        Ok(None)
53649    }
53650
53651    /// parse_table_alias - Ported from Python _parse_table_alias
53652    /// Parses table alias: AS alias [(col1, col2, ...)]
53653    #[allow(unused_variables, unused_mut)]
53654    pub fn parse_table_alias(&mut self) -> Result<Option<Expression>> {
53655        // Check for AS keyword (optional in most dialects)
53656        let has_as = self.match_token(TokenType::As);
53657
53658        // Handle AS (col1, col2) - no alias name, just column aliases
53659        if has_as && self.check(TokenType::LParen) {
53660            // Parse (col1, col2, ...)
53661            self.skip(); // consume LParen
53662            let mut cols = Vec::new();
53663            loop {
53664                if self.check(TokenType::RParen) {
53665                    break;
53666                }
53667                if let Ok(Some(col)) = self.parse_id_var() {
53668                    cols.push(col);
53669                }
53670                if !self.match_token(TokenType::Comma) {
53671                    break;
53672                }
53673            }
53674            self.expect(TokenType::RParen)?;
53675            return Ok(Some(Expression::TableAlias(Box::new(TableAlias {
53676                this: None,
53677                columns: cols,
53678            }))));
53679        }
53680
53681        // Parse the alias identifier
53682        // ClickHouse: keywords can be used as table aliases (e.g., AS select, AS from)
53683        let is_keyword_alias = has_as
53684            && matches!(
53685                self.config.dialect,
53686                Some(crate::dialects::DialectType::ClickHouse)
53687            )
53688            && self.peek().token_type.is_keyword();
53689        if !self.check(TokenType::Identifier)
53690            && !self.check(TokenType::QuotedIdentifier)
53691            && !self.check(TokenType::Var)
53692            && !is_keyword_alias
53693        {
53694            if has_as {
53695                return Err(self.parse_error("Expected identifier after AS"));
53696            }
53697            return Ok(None);
53698        }
53699
53700        let alias_token = self.advance();
53701        let is_quoted = alias_token.token_type == TokenType::QuotedIdentifier;
53702        let mut alias_ident = Identifier::new(alias_token.text.clone());
53703        if is_quoted {
53704            alias_ident.quoted = true;
53705        }
53706        let alias = Expression::Identifier(alias_ident);
53707
53708        // Check for column list: (col1, col2, ...)
53709        let columns = if self.match_token(TokenType::LParen) {
53710            let mut cols = Vec::new();
53711            loop {
53712                if self.check(TokenType::RParen) {
53713                    break;
53714                }
53715                if let Ok(Some(col)) = self.parse_id_var() {
53716                    cols.push(col);
53717                }
53718                if !self.match_token(TokenType::Comma) {
53719                    break;
53720                }
53721            }
53722            self.expect(TokenType::RParen)?;
53723            cols
53724        } else {
53725            Vec::new()
53726        };
53727
53728        Ok(Some(Expression::TableAlias(Box::new(TableAlias {
53729            this: Some(Box::new(alias)),
53730            columns,
53731        }))))
53732    }
53733
53734    /// parse_table_hints - Ported from Python _parse_table_hints
53735    /// Parses table hints (SQL Server WITH (...) or MySQL USE/IGNORE/FORCE INDEX)
53736    #[allow(unused_variables, unused_mut)]
53737    pub fn parse_table_hints(&mut self) -> Result<Option<Expression>> {
53738        let mut hints = Vec::new();
53739
53740        // SQL Server style: WITH (hint1, hint2, ...)
53741        if self.match_text_seq(&["WITH"]) && self.match_token(TokenType::LParen) {
53742            let mut expressions = Vec::new();
53743            loop {
53744                // Parse function or variable as hint
53745                if let Some(func) = self.parse_function()? {
53746                    expressions.push(func);
53747                } else if let Some(var) = self.parse_var()? {
53748                    expressions.push(var);
53749                } else {
53750                    break;
53751                }
53752                if !self.match_token(TokenType::Comma) {
53753                    break;
53754                }
53755            }
53756            self.match_token(TokenType::RParen);
53757
53758            if !expressions.is_empty() {
53759                hints.push(Expression::WithTableHint(Box::new(WithTableHint {
53760                    expressions,
53761                })));
53762            }
53763        } else {
53764            // MySQL style: USE INDEX, IGNORE INDEX, FORCE INDEX
53765            while self.match_texts(&["USE", "IGNORE", "FORCE"]) {
53766                let hint_type = self.previous().text.to_ascii_uppercase();
53767
53768                // Match INDEX or KEY
53769                let _ = self.match_texts(&["INDEX", "KEY"]);
53770
53771                // Check for optional FOR clause: FOR JOIN, FOR ORDER BY, FOR GROUP BY
53772                let target = if self.match_text_seq(&["FOR"]) {
53773                    let target_token = self.advance();
53774                    let target_text = target_token.text.to_ascii_uppercase();
53775                    // For ORDER BY and GROUP BY, combine into a single target name
53776                    let full_target = if (target_text == "ORDER" || target_text == "GROUP")
53777                        && self.check(TokenType::By)
53778                    {
53779                        self.skip(); // consume BY
53780                        format!("{} BY", target_text)
53781                    } else {
53782                        target_text
53783                    };
53784                    Some(Box::new(Expression::Identifier(Identifier {
53785                        name: full_target,
53786                        quoted: false,
53787                        trailing_comments: Vec::new(),
53788                        span: None,
53789                    })))
53790                } else {
53791                    None
53792                };
53793
53794                // Parse wrapped identifiers (index names)
53795                let expressions = if self.match_token(TokenType::LParen) {
53796                    let mut ids = Vec::new();
53797                    loop {
53798                        if let Some(id) = self.parse_id_var()? {
53799                            ids.push(id);
53800                        }
53801                        if !self.match_token(TokenType::Comma) {
53802                            break;
53803                        }
53804                    }
53805                    self.match_token(TokenType::RParen);
53806                    ids
53807                } else {
53808                    Vec::new()
53809                };
53810
53811                hints.push(Expression::IndexTableHint(Box::new(IndexTableHint {
53812                    this: Box::new(Expression::Identifier(Identifier {
53813                        name: hint_type,
53814                        quoted: false,
53815                        trailing_comments: Vec::new(),
53816                        span: None,
53817                    })),
53818                    expressions,
53819                    target,
53820                })));
53821            }
53822        }
53823
53824        if hints.is_empty() {
53825            return Ok(None);
53826        }
53827
53828        // Return as a Tuple containing hints
53829        Ok(Some(Expression::Tuple(Box::new(Tuple {
53830            expressions: hints,
53831        }))))
53832    }
53833
53834    /// Parse TSQL TRUNCATE table hints: WITH (PARTITIONS(1, 2 TO 5, 10 TO 20, 84))
53835    /// Unlike regular table hints, PARTITIONS arguments can contain TO ranges.
53836    pub fn parse_truncate_table_hints(&mut self) -> Result<Option<Expression>> {
53837        if !self.match_text_seq(&["WITH"]) || !self.match_token(TokenType::LParen) {
53838            return Ok(None);
53839        }
53840
53841        let mut hints = Vec::new();
53842
53843        // Check for PARTITIONS specifically
53844        if self.check_identifier("PARTITIONS") {
53845            self.skip(); // consume PARTITIONS
53846            self.expect(TokenType::LParen)?;
53847
53848            // Parse partition ranges: 1, 2 TO 5, 10 TO 20, 84
53849            let mut parts = Vec::new();
53850            loop {
53851                if self.check(TokenType::RParen) {
53852                    break;
53853                }
53854                let low = self.parse_primary()?;
53855                if self.match_text_seq(&["TO"]) {
53856                    let high = self.parse_primary()?;
53857                    parts.push(Expression::PartitionRange(Box::new(PartitionRange {
53858                        this: Box::new(low),
53859                        expression: Some(Box::new(high)),
53860                        expressions: Vec::new(),
53861                    })));
53862                } else {
53863                    parts.push(low);
53864                }
53865                if !self.match_token(TokenType::Comma) {
53866                    break;
53867                }
53868            }
53869            self.expect(TokenType::RParen)?; // close PARTITIONS(...)
53870
53871            // Create an Anonymous function for PARTITIONS(...)
53872            hints.push(Expression::Anonymous(Box::new(Anonymous {
53873                this: Box::new(Expression::Identifier(Identifier {
53874                    name: "PARTITIONS".to_string(),
53875                    quoted: false,
53876                    trailing_comments: Vec::new(),
53877                    span: None,
53878                })),
53879                expressions: parts,
53880            })));
53881        } else {
53882            // Fall back to regular hint parsing (function or var)
53883            loop {
53884                if let Some(func) = self.parse_function()? {
53885                    hints.push(func);
53886                } else if let Some(var) = self.parse_var()? {
53887                    hints.push(var);
53888                } else {
53889                    break;
53890                }
53891                if !self.match_token(TokenType::Comma) {
53892                    break;
53893                }
53894            }
53895        }
53896
53897        self.expect(TokenType::RParen)?; // close WITH(...)
53898
53899        if hints.is_empty() {
53900            return Ok(None);
53901        }
53902
53903        // Wrap in WithTableHint then Tuple (same as parse_table_hints)
53904        let hint = Expression::WithTableHint(Box::new(WithTableHint { expressions: hints }));
53905
53906        Ok(Some(Expression::Tuple(Box::new(Tuple {
53907            expressions: vec![hint],
53908        }))))
53909    }
53910
53911    /// parse_table_part - Parse a single part of a table reference
53912    /// Tries: identifier, string as identifier, placeholder
53913    #[allow(unused_variables, unused_mut)]
53914    pub fn parse_table_part(&mut self) -> Result<Option<Expression>> {
53915        // Try to parse an identifier
53916        if let Some(id) = self.parse_id_var()? {
53917            return Ok(Some(id));
53918        }
53919
53920        // Try to parse a string as identifier
53921        if let Some(str_id) = self.parse_string_as_identifier()? {
53922            return Ok(Some(str_id));
53923        }
53924
53925        // Try to parse a placeholder
53926        if let Some(placeholder) = self.parse_placeholder()? {
53927            return Ok(Some(placeholder));
53928        }
53929
53930        // Accept keywords as identifiers in table part context (e.g., db.cluster where "cluster" is a keyword)
53931        // This mirrors Python sqlglot's ID_VAR_TOKENS which includes many keyword types
53932        if self.check_keyword_as_identifier() {
53933            let text = self.peek().text.clone();
53934            self.skip();
53935            return Ok(Some(Expression::Identifier(Identifier {
53936                name: text,
53937                quoted: false,
53938                trailing_comments: Vec::new(),
53939                span: None,
53940            })));
53941        }
53942
53943        Ok(None)
53944    }
53945
53946    /// Check if the current token is a keyword that can be used as an identifier in certain contexts
53947    /// This includes many SQL keywords like CLUSTER, TABLE, INDEX, etc.
53948    fn check_keyword_as_identifier(&self) -> bool {
53949        if self.is_at_end() {
53950            return false;
53951        }
53952        let token_type = self.peek().token_type;
53953        // Keywords that can be used as identifiers (similar to Python's ID_VAR_TOKENS)
53954        matches!(
53955            token_type,
53956            TokenType::Cluster
53957                | TokenType::Table
53958                | TokenType::Index
53959                | TokenType::View
53960                | TokenType::Database
53961                | TokenType::Schema
53962                | TokenType::Column
53963                | TokenType::Function
53964                | TokenType::Procedure
53965                | TokenType::Constraint
53966                | TokenType::Sequence
53967                | TokenType::Type
53968                | TokenType::Partition
53969                | TokenType::Comment
53970                | TokenType::Cache
53971                | TokenType::Commit
53972                | TokenType::Begin
53973                | TokenType::End
53974                | TokenType::Set
53975                | TokenType::Show
53976                | TokenType::Describe
53977                | TokenType::Use
53978                | TokenType::Execute
53979                | TokenType::Delete
53980                | TokenType::Update
53981                | TokenType::Merge
53982                | TokenType::Load
53983                | TokenType::Copy
53984                | TokenType::Truncate
53985                | TokenType::Replace
53986                | TokenType::Refresh
53987                | TokenType::Rename
53988                | TokenType::Filter
53989                | TokenType::Format
53990                | TokenType::First
53991                | TokenType::Next
53992                | TokenType::Last
53993                | TokenType::Keep
53994                | TokenType::Match
53995                | TokenType::Over
53996                | TokenType::Range
53997                | TokenType::Rows
53998                | TokenType::Row
53999                | TokenType::Offset
54000                | TokenType::Limit
54001                | TokenType::Top
54002                | TokenType::Cube
54003                | TokenType::Rollup
54004                | TokenType::Pivot
54005                | TokenType::Unpivot
54006                | TokenType::Window
54007                | TokenType::Recursive
54008                | TokenType::Unique
54009                | TokenType::Temporary
54010                | TokenType::Volatile
54011                | TokenType::References
54012                | TokenType::Natural
54013                | TokenType::Left
54014                | TokenType::Right
54015                | TokenType::Full
54016                | TokenType::Semi
54017                | TokenType::Anti
54018                | TokenType::Apply
54019                | TokenType::All
54020                | TokenType::Asc
54021                | TokenType::Desc
54022                | TokenType::Analyze
54023        )
54024    }
54025
54026    /// parse_table_parts - Parse catalog.schema.table or schema.table or table
54027    /// Returns a Table expression with all parts
54028    #[allow(unused_variables, unused_mut)]
54029    pub fn parse_table_parts(&mut self) -> Result<Option<Expression>> {
54030        // Parse the first part
54031        let first = self.parse_table_part()?;
54032        if first.is_none() {
54033            return Ok(None);
54034        }
54035
54036        let mut parts = vec![first.unwrap()];
54037
54038        // Parse additional dot-separated parts
54039        while self.match_token(TokenType::Dot) {
54040            if let Some(part) = self.parse_table_part()? {
54041                parts.push(part);
54042            } else {
54043                break;
54044            }
54045        }
54046
54047        // Convert parts to Table expression
54048        // Last part is table name, second-to-last is schema, third-to-last is catalog
54049        let (catalog, schema, name) = match parts.len() {
54050            1 => (None, None, parts.pop().unwrap()),
54051            2 => {
54052                let table = parts.pop().unwrap();
54053                let schema = parts.pop().unwrap();
54054                (None, Some(schema), table)
54055            }
54056            _ => {
54057                let table = parts.pop().unwrap();
54058                let schema = parts.pop().unwrap();
54059                let catalog = parts.pop();
54060                (catalog, Some(schema), table)
54061            }
54062        };
54063
54064        // Extract identifier from Expression
54065        let name_ident = match name {
54066            Expression::Identifier(id) => id,
54067            _ => Identifier::new(String::new()),
54068        };
54069        let schema_ident = schema.map(|s| match s {
54070            Expression::Identifier(id) => id,
54071            _ => Identifier::new(String::new()),
54072        });
54073        let catalog_ident = catalog.map(|c| match c {
54074            Expression::Identifier(id) => id,
54075            _ => Identifier::new(String::new()),
54076        });
54077
54078        Ok(Some(Expression::boxed_table(TableRef {
54079            name: name_ident,
54080            schema: schema_ident,
54081            catalog: catalog_ident,
54082            alias: None,
54083            alias_explicit_as: false,
54084            column_aliases: Vec::new(),
54085            trailing_comments: Vec::new(),
54086            when: None,
54087            only: false,
54088            final_: false,
54089            table_sample: None,
54090            hints: Vec::new(),
54091            system_time: None,
54092            partitions: Vec::new(),
54093            identifier_func: None,
54094            changes: None,
54095            version: None,
54096            span: None,
54097        })))
54098    }
54099
54100    /// parse_table_sample - Implemented from Python _parse_table_sample
54101    /// Calls: parse_number, parse_factor, parse_placeholder
54102    #[allow(unused_variables, unused_mut)]
54103    pub fn parse_table_sample(&mut self) -> Result<Option<Expression>> {
54104        if self.match_text_seq(&["USING", "SAMPLE"]) {
54105            return Ok(Some(Expression::TableSample(Box::new(TableSample {
54106                this: None,
54107                sample: None,
54108                expressions: Vec::new(),
54109                method: None,
54110                bucket_numerator: None,
54111                bucket_denominator: None,
54112                bucket_field: None,
54113                percent: None,
54114                rows: None,
54115                size: None,
54116                seed: None,
54117            }))));
54118        }
54119        if self.match_text_seq(&["BUCKET"]) {
54120            // Matched: BUCKET
54121            return Ok(None);
54122        }
54123        if self.match_text_seq(&["OUT", "OF"]) {
54124            // Matched: OUT OF
54125            return Ok(None);
54126        }
54127        if self.match_texts(&["SEED", "REPEATABLE"]) {
54128            // Matched one of: SEED, REPEATABLE
54129            return Ok(None);
54130        }
54131        Ok(None)
54132    }
54133
54134    /// parse_term - Parses addition/subtraction expressions (+ - operators)
54135    /// Python: _parse_term
54136    /// Delegates to the existing parse_addition in the operator precedence chain
54137    pub fn parse_term(&mut self) -> Result<Option<Expression>> {
54138        // Delegate to the existing addition parsing
54139        match self.parse_addition() {
54140            Ok(expr) => Ok(Some(expr)),
54141            Err(_) => Ok(None),
54142        }
54143    }
54144
54145    /// parse_to_table - ClickHouse TO table property
54146    /// Parses: TO table_name
54147    #[allow(unused_variables, unused_mut)]
54148    pub fn parse_to_table(&mut self) -> Result<Option<Expression>> {
54149        // Parse the table reference
54150        let table = self.parse_table_parts()?;
54151        if table.is_none() {
54152            return Ok(None);
54153        }
54154
54155        Ok(Some(Expression::ToTableProperty(Box::new(
54156            ToTableProperty {
54157                this: Box::new(table.unwrap()),
54158            },
54159        ))))
54160    }
54161
54162    /// parse_tokens - Operator precedence parser
54163    #[allow(unused_variables, unused_mut)]
54164    pub fn parse_tokens(&mut self) -> Result<Option<Expression>> {
54165        // Uses operator precedence parsing pattern
54166        Ok(None)
54167    }
54168
54169    /// parse_trim - Ported from Python _parse_trim
54170    /// Parses TRIM function: TRIM([BOTH|LEADING|TRAILING] chars FROM str) or TRIM(str, chars)
54171    #[allow(unused_variables, unused_mut)]
54172    pub fn parse_trim(&mut self) -> Result<Option<Expression>> {
54173        // Check for position keyword (BOTH, LEADING, TRAILING)
54174        let (position, position_explicit) = if self.match_texts(&["BOTH"]) {
54175            (TrimPosition::Both, true)
54176        } else if self.match_texts(&["LEADING"]) {
54177            (TrimPosition::Leading, true)
54178        } else if self.match_texts(&["TRAILING"]) {
54179            (TrimPosition::Trailing, true)
54180        } else {
54181            (TrimPosition::Both, false)
54182        };
54183
54184        // Parse first expression
54185        let first = match self.parse_bitwise() {
54186            Ok(Some(expr)) => self.try_clickhouse_func_arg_alias(expr),
54187            Ok(None) => return Ok(None),
54188            Err(e) => return Err(e),
54189        };
54190
54191        // Check for FROM or comma to see if there's a second expression
54192        let (this, characters, sql_standard_syntax) = if self.match_token(TokenType::From) {
54193            // SQL standard syntax: TRIM([position] chars FROM str)
54194            let second = match self.parse_bitwise() {
54195                Ok(Some(expr)) => self.try_clickhouse_func_arg_alias(expr),
54196                Ok(None) => return Err(self.parse_error("Expected expression after FROM in TRIM")),
54197                Err(e) => return Err(e),
54198            };
54199            // In SQL standard syntax: first is characters, second is the string
54200            (second, Some(first), true)
54201        } else if self.match_token(TokenType::Comma) {
54202            // Function syntax: TRIM(a, b)
54203            let second = match self.parse_bitwise() {
54204                Ok(Some(expr)) => Some(expr),
54205                Ok(None) => None,
54206                Err(e) => return Err(e),
54207            };
54208            // In Spark, comma syntax is TRIM(chars, str) - pattern first
54209            // In other dialects, comma syntax is TRIM(str, chars) - string first
54210            let trim_pattern_first = matches!(
54211                self.config.dialect,
54212                Some(crate::dialects::DialectType::Spark)
54213            );
54214            if trim_pattern_first && second.is_some() {
54215                // first=chars, second=str
54216                (second.unwrap(), Some(first), false)
54217            } else {
54218                (first, second, false)
54219            }
54220        } else {
54221            // Single argument: TRIM(str)
54222            (first, None, false)
54223        };
54224
54225        Ok(Some(Expression::Trim(Box::new(TrimFunc {
54226            this,
54227            characters,
54228            position,
54229            sql_standard_syntax,
54230            position_explicit,
54231        }))))
54232    }
54233
54234    /// parse_truncate_table - Implemented from Python _parse_truncate_table
54235    /// Calls: parse_on_property, parse_partition, parse_function
54236    #[allow(unused_variables, unused_mut)]
54237    pub fn parse_truncate_table(&mut self) -> Result<Option<Expression>> {
54238        if self.match_text_seq(&["RESTART", "IDENTITY"]) {
54239            return Ok(Some(Expression::TruncateTable(Box::new(TruncateTable {
54240                expressions: Vec::new(),
54241                is_database: None,
54242                exists: false,
54243                only: None,
54244                cluster: None,
54245                identity: None,
54246                option: None,
54247                partition: None,
54248            }))));
54249        }
54250        if self.match_text_seq(&["CONTINUE", "IDENTITY"]) {
54251            // Matched: CONTINUE IDENTITY
54252            return Ok(None);
54253        }
54254        if self.match_text_seq(&["CASCADE"]) {
54255            // Matched: CASCADE
54256            return Ok(None);
54257        }
54258        Ok(None)
54259    }
54260
54261    /// parse_ttl - Implemented from Python _parse_ttl
54262    /// Parses ClickHouse TTL expression with optional DELETE, RECOMPRESS, TO DISK/VOLUME
54263    pub fn parse_ttl(&mut self) -> Result<Option<Expression>> {
54264        // Parse CSV of TTL actions
54265        let mut expressions = Vec::new();
54266
54267        loop {
54268            // Parse the base expression
54269            let base_start = self.current;
54270            let this = match self.parse_bitwise() {
54271                Ok(Some(expr)) => expr,
54272                _ => {
54273                    self.current = base_start;
54274                    let mut paren_depth = 0usize;
54275                    while !self.is_at_end() {
54276                        if paren_depth == 0
54277                            && (self.check(TokenType::Comma)
54278                                || self.peek().text.eq_ignore_ascii_case("DELETE")
54279                                || self.peek().text.eq_ignore_ascii_case("RECOMPRESS")
54280                                || self.peek().text.eq_ignore_ascii_case("TO")
54281                                || self.peek().text.eq_ignore_ascii_case("WHERE")
54282                                || self.peek().text.eq_ignore_ascii_case("GROUP")
54283                                || self.peek().text.eq_ignore_ascii_case("SET"))
54284                        {
54285                            break;
54286                        }
54287                        if self.check(TokenType::LParen) {
54288                            paren_depth += 1;
54289                        } else if self.check(TokenType::RParen) {
54290                            if paren_depth == 0 {
54291                                break;
54292                            }
54293                            paren_depth -= 1;
54294                        }
54295                        self.skip();
54296                    }
54297                    if self.current == base_start {
54298                        break;
54299                    }
54300                    let raw = self
54301                        .tokens_to_sql(base_start, self.current)
54302                        .trim()
54303                        .to_string();
54304                    Expression::Var(Box::new(Var { this: raw }))
54305                }
54306            };
54307
54308            // Check for TTL action
54309            let action = if self.match_text_seq(&["DELETE"]) {
54310                Expression::MergeTreeTTLAction(Box::new(MergeTreeTTLAction {
54311                    this: Box::new(this),
54312                    delete: Some(Box::new(Expression::Boolean(BooleanLiteral {
54313                        value: true,
54314                    }))),
54315                    recompress: None,
54316                    to_disk: None,
54317                    to_volume: None,
54318                }))
54319            } else if self.match_text_seq(&["RECOMPRESS"]) {
54320                let recompress = if self.match_identifier("CODEC") {
54321                    self.expect(TokenType::LParen)?;
54322                    let mut args = Vec::new();
54323                    if !self.check(TokenType::RParen) {
54324                        args.push(self.parse_expression()?);
54325                        while self.match_token(TokenType::Comma) {
54326                            args.push(self.parse_expression()?);
54327                        }
54328                    }
54329                    self.expect(TokenType::RParen)?;
54330                    Some(Box::new(Expression::Function(Box::new(Function::new(
54331                        "CODEC".to_string(),
54332                        args,
54333                    )))))
54334                } else {
54335                    self.parse_bitwise()?.map(Box::new)
54336                };
54337                Expression::MergeTreeTTLAction(Box::new(MergeTreeTTLAction {
54338                    this: Box::new(this),
54339                    delete: None,
54340                    recompress,
54341                    to_disk: None,
54342                    to_volume: None,
54343                }))
54344            } else if self.match_text_seq(&["TO", "DISK"]) {
54345                let to_disk = self.parse_string()?.map(Box::new);
54346                Expression::MergeTreeTTLAction(Box::new(MergeTreeTTLAction {
54347                    this: Box::new(this),
54348                    delete: None,
54349                    recompress: None,
54350                    to_disk,
54351                    to_volume: None,
54352                }))
54353            } else if self.match_text_seq(&["TO", "VOLUME"]) {
54354                let to_volume = self.parse_string()?.map(Box::new);
54355                Expression::MergeTreeTTLAction(Box::new(MergeTreeTTLAction {
54356                    this: Box::new(this),
54357                    delete: None,
54358                    recompress: None,
54359                    to_disk: None,
54360                    to_volume,
54361                }))
54362            } else {
54363                this
54364            };
54365
54366            expressions.push(action);
54367
54368            if !self.match_token(TokenType::Comma) {
54369                break;
54370            }
54371        }
54372
54373        // Parse optional top-level WHERE clause (for backwards compatibility)
54374        let where_ = self.parse_where()?.map(Box::new);
54375
54376        // Parse optional GROUP BY
54377        let group = if self.match_token(TokenType::Group) {
54378            self.expect(TokenType::By)?;
54379            let mut exprs = Vec::new();
54380            exprs.push(self.parse_expression()?);
54381            while self.match_token(TokenType::Comma) {
54382                exprs.push(self.parse_expression()?);
54383            }
54384            Some(Box::new(Expression::Group(Box::new(Group {
54385                expressions: exprs,
54386                grouping_sets: None,
54387                cube: None,
54388                rollup: None,
54389                totals: None,
54390                all: None,
54391            }))))
54392        } else {
54393            None
54394        };
54395
54396        // Parse optional SET (aggregates) after GROUP BY
54397        let aggregates = if group.is_some() && self.match_token(TokenType::Set) {
54398            let mut aggs = Vec::new();
54399            loop {
54400                aggs.push(self.parse_expression()?);
54401                if !self.match_token(TokenType::Comma) {
54402                    break;
54403                }
54404            }
54405            if aggs.is_empty() {
54406                None
54407            } else {
54408                Some(Box::new(Expression::Tuple(Box::new(Tuple {
54409                    expressions: aggs,
54410                }))))
54411            }
54412        } else {
54413            None
54414        };
54415
54416        Ok(Some(Expression::MergeTreeTTL(Box::new(MergeTreeTTL {
54417            expressions,
54418            where_,
54419            group,
54420            aggregates,
54421        }))))
54422    }
54423
54424    /// parse_type - Parses a data type expression
54425    /// Python: _parse_type
54426    pub fn parse_type(&mut self) -> Result<Option<Expression>> {
54427        // First try to parse an interval
54428        if let Some(interval) = self.parse_interval()? {
54429            return self.parse_column_ops_with_expr(Some(interval));
54430        }
54431
54432        // Try to parse a data type
54433        let data_type = self.parse_types()?;
54434
54435        if let Some(dt) = data_type {
54436            // If it's a Cast (BigQuery inline constructor), apply column ops
54437            if matches!(dt, Expression::Cast(_)) {
54438                return self.parse_column_ops_with_expr(Some(dt));
54439            }
54440
54441            // Try to parse a primary expression after the type
54442            let start_pos = self.current;
54443            if let Some(primary) = self.parse_primary_or_var()? {
54444                // If it's a literal, this might be a type cast like DATE '2020-01-01'
54445                if let Expression::Literal(_) = &primary {
54446                    let result = self.parse_column_ops_with_expr(Some(primary))?;
54447                    if let Some(value) = result {
54448                        // Create a Cast expression
54449                        if let Expression::DataType(data_type_struct) = dt {
54450                            return Ok(Some(Expression::Cast(Box::new(Cast {
54451                                this: value,
54452                                to: data_type_struct,
54453                                trailing_comments: Vec::new(),
54454                                double_colon_syntax: false,
54455                                format: None,
54456                                default: None,
54457                                inferred_type: None,
54458                            }))));
54459                        }
54460                    }
54461                }
54462                // Backtrack if not a type-literal pattern
54463                self.current = start_pos;
54464            }
54465
54466            return Ok(Some(dt));
54467        }
54468
54469        Ok(None)
54470    }
54471
54472    /// parse_type_size - Ported from Python _parse_type_size
54473    /// Parses type size parameters like 10 in VARCHAR(10) or 10, 2 in DECIMAL(10, 2)
54474    #[allow(unused_variables, unused_mut)]
54475    pub fn parse_type_size(&mut self) -> Result<Option<Expression>> {
54476        // First try to parse a type - this handles both numeric literals and type names
54477        let this = self.parse_type()?;
54478
54479        if this.is_none() {
54480            return Ok(None);
54481        }
54482
54483        let mut result = this.unwrap();
54484
54485        // If it's a Column with no table, convert it to an Identifier (var)
54486        // This handles cases like CHAR in VARCHAR(100 CHAR)
54487        if let Expression::Column(ref col) = result {
54488            if col.table.is_none() {
54489                result = Expression::Identifier(col.name.clone());
54490            }
54491        }
54492
54493        // Check for optional expression after the type (e.g., "CHAR" in "100 CHAR")
54494        // This is for byte/char length specifiers in some dialects
54495        if let Some(var_token) = self.parse_var()? {
54496            // We have an additional specifier, combine them
54497            // For now, just return the original result since Rust doesn't have DataTypeParam
54498            // The var expression would be attached as an expression in Python
54499        }
54500
54501        Ok(Some(result))
54502    }
54503
54504    /// parse_types - Implemented from Python _parse_types
54505    /// Calls: parse_string
54506    #[allow(unused_variables, unused_mut)]
54507    pub fn parse_types(&mut self) -> Result<Option<Expression>> {
54508        if self.match_text_seq(&["SYSUDTLIB", "."]) {
54509            return Ok(Some(Expression::Identifier(Identifier {
54510                name: String::new(),
54511                quoted: false,
54512                trailing_comments: Vec::new(),
54513                span: None,
54514            })));
54515        }
54516        if self.match_text_seq(&["WITH", "TIME", "ZONE"]) {
54517            // Matched: WITH TIME ZONE
54518            return Ok(None);
54519        }
54520        if self.match_text_seq(&["WITH", "LOCAL", "TIME", "ZONE"]) {
54521            // Matched: WITH LOCAL TIME ZONE
54522            return Ok(None);
54523        }
54524        Ok(None)
54525    }
54526
54527    /// parse_unique - Implemented from Python _parse_unique
54528    /// Parses UNIQUE [KEY|INDEX] [NULLS NOT DISTINCT] [(columns)] [USING index_type]
54529    #[allow(unused_variables, unused_mut)]
54530    pub fn parse_unique(&mut self) -> Result<Option<Expression>> {
54531        // Check for optional KEY/INDEX
54532        let _ = self.match_texts(&["KEY", "INDEX"]);
54533
54534        // Check for NULLS NOT DISTINCT (PostgreSQL 15+ feature)
54535        let nulls = if self.match_text_seq(&["NULLS", "NOT", "DISTINCT"]) {
54536            Some(Box::new(Expression::Boolean(BooleanLiteral {
54537                value: true,
54538            })))
54539        } else {
54540            None
54541        };
54542
54543        // Parse the optional key name and schema (column list)
54544        let unique_key = self.parse_unique_key()?;
54545        let this = self.parse_schema_with_this(unique_key)?;
54546
54547        // Parse optional USING index_type
54548        let index_type = if self.match_token(TokenType::Using) {
54549            self.skip();
54550            Some(Box::new(Expression::Var(Box::new(Var {
54551                this: self.previous().text.clone(),
54552            }))))
54553        } else {
54554            None
54555        };
54556
54557        Ok(Some(Expression::UniqueColumnConstraint(Box::new(
54558            UniqueColumnConstraint {
54559                this: this.map(Box::new),
54560                index_type,
54561                on_conflict: None,
54562                nulls,
54563                options: Vec::new(),
54564            },
54565        ))))
54566    }
54567
54568    /// parse_unique_key - Parse the key/index name for UNIQUE constraint
54569    /// Simply parses an identifier
54570    #[allow(unused_variables, unused_mut)]
54571    pub fn parse_unique_key(&mut self) -> Result<Option<Expression>> {
54572        self.parse_id_var()
54573    }
54574
54575    /// parse_unnest - Ported from Python _parse_unnest
54576    /// Parses UNNEST(array_expr) [WITH ORDINALITY] [AS alias]
54577    #[allow(unused_variables, unused_mut)]
54578    pub fn parse_unnest(&mut self) -> Result<Option<Expression>> {
54579        // Check for UNNEST keyword
54580        if !self.match_texts(&["UNNEST"]) {
54581            return Ok(None);
54582        }
54583
54584        // Expect opening parenthesis
54585        if !self.match_token(TokenType::LParen) {
54586            return Ok(None);
54587        }
54588
54589        // Parse comma-separated array expression(s): UNNEST(arr1, arr2, ...)
54590        let this = match self.parse_expression() {
54591            Ok(expr) => expr,
54592            Err(e) => return Err(e),
54593        };
54594
54595        let mut extra_expressions = Vec::new();
54596        while self.match_token(TokenType::Comma) {
54597            let expr = self.parse_expression()?;
54598            extra_expressions.push(expr);
54599        }
54600
54601        // Expect closing parenthesis
54602        self.expect(TokenType::RParen)?;
54603
54604        // Check for WITH ORDINALITY
54605        let with_ordinality = self.match_text_seq(&["WITH", "ORDINALITY"]);
54606
54607        // Parse optional alias
54608        let alias = if self.match_token(TokenType::As)
54609            || self.check(TokenType::Identifier)
54610            || self.check(TokenType::QuotedIdentifier)
54611        {
54612            if self.check(TokenType::Identifier) || self.check(TokenType::QuotedIdentifier) {
54613                let is_quoted = self.check(TokenType::QuotedIdentifier);
54614                let token = self.advance();
54615                let mut ident = Identifier::new(token.text.clone());
54616                if is_quoted {
54617                    ident.quoted = true;
54618                }
54619                Some(ident)
54620            } else {
54621                None
54622            }
54623        } else {
54624            None
54625        };
54626
54627        Ok(Some(Expression::Unnest(Box::new(UnnestFunc {
54628            this,
54629            expressions: extra_expressions,
54630            with_ordinality,
54631            alias,
54632            offset_alias: None,
54633        }))))
54634    }
54635
54636    /// parse_unpivot_columns - Implemented from Python _parse_unpivot_columns
54637    /// Python: parser.py:4454-4462
54638    /// Parses INTO NAME column VALUE col1, col2, ...
54639    #[allow(unused_variables, unused_mut)]
54640    pub fn parse_unpivot_columns(&mut self) -> Result<Option<Expression>> {
54641        // Must match INTO keyword
54642        if !self.match_token(TokenType::Into) {
54643            return Ok(None);
54644        }
54645
54646        // Parse NAME column
54647        let this = if self.match_text_seq(&["NAME"]) {
54648            self.parse_column()?
54649        } else {
54650            None
54651        };
54652
54653        // Parse VALUE columns
54654        let expressions = if self.match_text_seq(&["VALUE"]) {
54655            let mut cols = Vec::new();
54656            loop {
54657                if let Some(col) = self.parse_column()? {
54658                    cols.push(col);
54659                }
54660                if !self.match_token(TokenType::Comma) {
54661                    break;
54662                }
54663            }
54664            cols
54665        } else {
54666            Vec::new()
54667        };
54668
54669        // If we have either this or expressions, return an UnpivotColumns
54670        if this.is_some() || !expressions.is_empty() {
54671            Ok(Some(Expression::UnpivotColumns(Box::new(UnpivotColumns {
54672                this: Box::new(this.unwrap_or(Expression::Null(Null))),
54673                expressions,
54674            }))))
54675        } else {
54676            Ok(None)
54677        }
54678    }
54679
54680    /// parse_unquoted_field - Parses a field and converts unquoted identifiers to Var
54681    /// Python: _parse_unquoted_field
54682    pub fn parse_unquoted_field(&mut self) -> Result<Option<Expression>> {
54683        let field = self.parse_field()?;
54684
54685        // If field is an unquoted identifier, convert it to a Var
54686        match field {
54687            Some(Expression::Identifier(id)) if !id.quoted => {
54688                Ok(Some(Expression::Var(Box::new(Var { this: id.name }))))
54689            }
54690            other => Ok(other),
54691        }
54692    }
54693
54694    /// parse_user_defined_function - Parses user-defined function call
54695    /// Python: _parse_user_defined_function
54696    /// Parses: schema.function_name(param1, param2, ...)
54697    pub fn parse_user_defined_function(&mut self) -> Result<Option<Expression>> {
54698        // Parse table parts (potentially schema-qualified function name)
54699        let this = self.parse_table_parts()?;
54700        if this.is_none() {
54701            return Ok(None);
54702        }
54703
54704        // If no L_PAREN, return just the table parts (not a function call)
54705        if !self.match_token(TokenType::LParen) {
54706            return Ok(this);
54707        }
54708
54709        // Parse function parameters
54710        let mut expressions = Vec::new();
54711        if !self.check(TokenType::RParen) {
54712            loop {
54713                if let Some(param) = self.parse_function_parameter()? {
54714                    expressions.push(param);
54715                }
54716                if !self.match_token(TokenType::Comma) {
54717                    break;
54718                }
54719            }
54720        }
54721
54722        self.match_token(TokenType::RParen);
54723
54724        Ok(Some(Expression::UserDefinedFunction(Box::new(
54725            UserDefinedFunction {
54726                this: Box::new(this.unwrap()),
54727                expressions,
54728                wrapped: Some(Box::new(Expression::Boolean(BooleanLiteral {
54729                    value: true,
54730                }))),
54731            },
54732        ))))
54733    }
54734
54735    /// parse_user_defined_function_expression - Parse user-defined function expression
54736    #[allow(unused_variables, unused_mut)]
54737    pub fn parse_user_defined_function_expression(&mut self) -> Result<Option<Expression>> {
54738        // Parse a statement and wrap in Some if successful
54739        match self.parse_statement() {
54740            Ok(stmt) => Ok(Some(stmt)),
54741            Err(_) => Ok(None),
54742        }
54743    }
54744
54745    /// parse_user_defined_type - Parses a user-defined type reference
54746    /// Python: _parse_user_defined_type
54747    /// Format: schema.type_name or just type_name
54748    pub fn parse_user_defined_type(
54749        &mut self,
54750        identifier: Identifier,
54751    ) -> Result<Option<Expression>> {
54752        let mut type_name = identifier.name.clone();
54753
54754        // Handle dotted names (schema.type_name)
54755        while self.match_token(TokenType::Dot) {
54756            if !self.is_at_end() {
54757                let token = self.advance();
54758                type_name = format!("{}.{}", type_name, token.text);
54759            } else {
54760                break;
54761            }
54762        }
54763
54764        // Return as a custom data type
54765        Ok(Some(Expression::DataType(DataType::Custom {
54766            name: type_name,
54767        })))
54768    }
54769
54770    /// parse_using_identifiers - Ported from Python _parse_using_identifiers
54771    /// Parses (col1, col2, ...) for JOIN USING clause
54772    #[allow(unused_variables, unused_mut)]
54773    pub fn parse_using_identifiers(&mut self) -> Result<Option<Expression>> {
54774        // Optionally expect opening paren
54775        let has_paren = self.match_token(TokenType::LParen);
54776
54777        let mut identifiers = Vec::new();
54778        loop {
54779            // Parse column as identifier
54780            if let Some(expr) = self.parse_identifier()? {
54781                identifiers.push(expr);
54782            } else {
54783                break;
54784            }
54785            if !self.match_token(TokenType::Comma) {
54786                break;
54787            }
54788        }
54789
54790        // Match closing paren if we matched opening
54791        if has_paren {
54792            self.expect(TokenType::RParen)?;
54793        }
54794
54795        if identifiers.is_empty() {
54796            Ok(None)
54797        } else {
54798            Ok(Some(Expression::Tuple(Box::new(Tuple {
54799                expressions: identifiers,
54800            }))))
54801        }
54802    }
54803
54804    /// parse_value - Parses a value tuple for INSERT VALUES clause
54805    /// Python: _parse_value
54806    /// Syntax: (expr1, expr2, ...) or just expr (single value)
54807    pub fn parse_value(&mut self) -> Result<Option<Expression>> {
54808        // Check for parenthesized list of expressions
54809        if self.match_token(TokenType::LParen) {
54810            let mut expressions = Vec::new();
54811
54812            if !self.check(TokenType::RParen) {
54813                loop {
54814                    // Support DEFAULT keyword in VALUES
54815                    if self.match_texts(&["DEFAULT"]) {
54816                        let text = self.previous().text.to_ascii_uppercase();
54817                        expressions.push(Expression::Var(Box::new(Var { this: text })));
54818                    } else {
54819                        // Try to parse an expression
54820                        let saved_pos = self.current;
54821                        match self.parse_expression() {
54822                            Ok(expr) => expressions.push(expr),
54823                            Err(_) => {
54824                                self.current = saved_pos;
54825                            }
54826                        }
54827                    }
54828
54829                    if !self.match_token(TokenType::Comma) {
54830                        break;
54831                    }
54832                }
54833            }
54834
54835            self.match_token(TokenType::RParen);
54836            return Ok(Some(Expression::Tuple(Box::new(Tuple { expressions }))));
54837        }
54838
54839        // Single value without parentheses (some dialects support VALUES 1, 2)
54840        let saved_pos = self.current;
54841        match self.parse_expression() {
54842            Ok(expr) => {
54843                return Ok(Some(Expression::Tuple(Box::new(Tuple {
54844                    expressions: vec![expr],
54845                }))));
54846            }
54847            Err(_) => {
54848                self.current = saved_pos;
54849            }
54850        }
54851
54852        Ok(None)
54853    }
54854
54855    /// parse_var - Parse variable reference (unquoted identifier)
54856    /// Python: if self._match(TokenType.VAR): return exp.Var(this=self._prev.text)
54857    pub fn parse_var(&mut self) -> Result<Option<Expression>> {
54858        if self.match_token(TokenType::Var) {
54859            let text = self.previous().text.clone();
54860            return Ok(Some(Expression::Var(Box::new(Var { this: text }))));
54861        }
54862        // Fall back to placeholder parsing
54863        self.parse_placeholder()
54864    }
54865
54866    /// parse_var_from_options - Ported from Python _parse_var_from_options
54867    /// Parses a variable/identifier from a predefined set of options
54868    #[allow(unused_variables, unused_mut)]
54869    pub fn parse_var_from_options(&mut self) -> Result<Option<Expression>> {
54870        // Without the options dict, we just try to parse an identifier
54871        if self.is_at_end() {
54872            return Ok(None);
54873        }
54874
54875        // Get current token text as the option
54876        let token = self.peek().clone();
54877        if token.token_type == TokenType::Identifier || token.token_type == TokenType::Var {
54878            self.skip();
54879            return Ok(Some(Expression::Var(Box::new(Var {
54880                this: token.text.to_ascii_uppercase(),
54881            }))));
54882        }
54883
54884        Ok(None)
54885    }
54886
54887    /// parse_var_or_string - Delegates to parse_string
54888    #[allow(unused_variables, unused_mut)]
54889    /// parse_var_or_string - Parses a string literal or a variable
54890    /// Python: parser.py:7506-7507
54891    pub fn parse_var_or_string(&mut self) -> Result<Option<Expression>> {
54892        // Try string first, then var
54893        if let Some(s) = self.parse_string()? {
54894            return Ok(Some(s));
54895        }
54896        self.parse_var_any_token()
54897    }
54898
54899    /// parse_vector_expressions - Transforms vector type parameters
54900    /// Python: _parse_vector_expressions
54901    /// In Python, this transforms a list of expressions where the first element (identifier)
54902    /// is converted to a DataType. In Rust, since VECTOR type parsing is handled inline in
54903    /// parse_data_type, this method parses vector expressions (element_type, dimension) from
54904    /// the current position and returns them as a Tuple.
54905    pub fn parse_vector_expressions(&mut self) -> Result<Option<Expression>> {
54906        let mut expressions = Vec::new();
54907
54908        // Parse element type - convert identifier to DataType
54909        if let Some(type_expr) = self.parse_type()? {
54910            expressions.push(type_expr);
54911        } else {
54912            return Ok(None);
54913        }
54914
54915        // Parse optional dimension or additional parameters
54916        while self.match_token(TokenType::Comma) {
54917            if let Some(expr) = self.parse_primary_or_var()? {
54918                expressions.push(expr);
54919            }
54920        }
54921
54922        if expressions.is_empty() {
54923            return Ok(None);
54924        }
54925
54926        Ok(Some(Expression::Tuple(Box::new(Tuple { expressions }))))
54927    }
54928
54929    /// parse_version - Implemented from Python _parse_version
54930    /// Python: parser.py:4266-4295
54931    /// Parses FOR SYSTEM_TIME AS OF, VERSIONS BETWEEN, etc.
54932    #[allow(unused_variables, unused_mut)]
54933    pub fn parse_version(&mut self) -> Result<Option<Expression>> {
54934        // Check for TIMESTAMP or VERSION snapshot token
54935        let this = if self.match_token(TokenType::TimestampSnapshot) {
54936            "TIMESTAMP".to_string()
54937        } else if self.match_token(TokenType::VersionSnapshot) {
54938            "VERSION".to_string()
54939        } else {
54940            return Ok(None);
54941        };
54942
54943        // Parse the kind and expression
54944        let (kind, expression) = if self.match_texts(&["FROM", "BETWEEN"]) {
54945            // FROM start TO end or BETWEEN start AND end
54946            let kind_str = self.previous().text.to_ascii_uppercase();
54947            let start = self.parse_bitwise()?;
54948            self.match_texts(&["TO", "AND"]);
54949            let end = self.parse_bitwise()?;
54950            let tuple = Expression::Tuple(Box::new(Tuple {
54951                expressions: vec![
54952                    start.unwrap_or(Expression::Null(Null)),
54953                    end.unwrap_or(Expression::Null(Null)),
54954                ],
54955            }));
54956            (kind_str, Some(Box::new(tuple)))
54957        } else if self.match_text_seq(&["CONTAINED", "IN"]) {
54958            // CONTAINED IN (values)
54959            let expressions = if self.match_token(TokenType::LParen) {
54960                let exprs = self.parse_expression_list()?;
54961                self.expect(TokenType::RParen)?;
54962                exprs
54963            } else {
54964                Vec::new()
54965            };
54966            (
54967                "CONTAINED IN".to_string(),
54968                Some(Box::new(Expression::Tuple(Box::new(Tuple { expressions })))),
54969            )
54970        } else if self.match_token(TokenType::All) {
54971            // ALL
54972            ("ALL".to_string(), None)
54973        } else {
54974            // AS OF
54975            self.match_text_seq(&["AS", "OF"]);
54976            let type_expr = self.parse_type()?;
54977            ("AS OF".to_string(), type_expr.map(Box::new))
54978        };
54979
54980        Ok(Some(Expression::Version(Box::new(Version {
54981            this: Box::new(Expression::Var(Box::new(Var { this }))),
54982            kind,
54983            expression,
54984        }))))
54985    }
54986
54987    /// parse_volatile_property - Parses VOLATILE property
54988    /// Python: _parse_volatile_property
54989    /// Returns VolatileProperty for table volatility or StabilityProperty for function stability
54990    pub fn parse_volatile_property(&mut self) -> Result<Option<Expression>> {
54991        // Check the token before VOLATILE to determine context
54992        // In SQL, VOLATILE can mean:
54993        // 1. Table volatility (CREATE VOLATILE TABLE)
54994        // 2. Function stability (CREATE FUNCTION ... VOLATILE)
54995
54996        // Look back to see if this is in a table context
54997        // PRE_VOLATILE_TOKENS typically include: CREATE, REPLACE, GLOBAL, etc.
54998        let is_table_context = if self.current >= 2 {
54999            let pre_token = &self.tokens[self.current - 2];
55000            matches!(
55001                pre_token.token_type,
55002                TokenType::Create | TokenType::Global | TokenType::Temporary | TokenType::Replace
55003            )
55004        } else {
55005            false
55006        };
55007
55008        if is_table_context {
55009            Ok(Some(Expression::VolatileProperty(Box::new(
55010                VolatileProperty { this: None },
55011            ))))
55012        } else {
55013            // Function stability - return StabilityProperty with "VOLATILE" literal
55014            Ok(Some(Expression::StabilityProperty(Box::new(
55015                StabilityProperty {
55016                    this: Box::new(Expression::Literal(Literal::String("VOLATILE".to_string()))),
55017                },
55018            ))))
55019        }
55020    }
55021
55022    /// parse_when_matched - Implemented from Python _parse_when_matched
55023    /// Calls: parse_disjunction, parse_star, parse_value
55024    #[allow(unused_variables, unused_mut)]
55025    /// Parse WHEN [NOT] MATCHED clauses for MERGE statements
55026    /// This is the public entry point that calls parse_when_matched_clauses
55027    pub fn parse_when_matched(&mut self) -> Result<Option<Expression>> {
55028        self.parse_when_matched_clauses()
55029    }
55030
55031    /// parse_where - Parse WHERE clause
55032    /// Python: if not self._match(TokenType.WHERE): return None; return exp.Where(this=self._parse_disjunction())
55033    pub fn parse_where(&mut self) -> Result<Option<Expression>> {
55034        if !self.match_token(TokenType::Where) {
55035            return Ok(None);
55036        }
55037        // Parse the condition expression
55038        let condition = self.parse_expression()?;
55039        Ok(Some(Expression::Where(Box::new(Where { this: condition }))))
55040    }
55041
55042    /// parse_window - Implemented from Python _parse_window
55043    /// Calls: parse_window_spec, parse_partition_and_order
55044    #[allow(unused_variables, unused_mut)]
55045    pub fn parse_window(&mut self) -> Result<Option<Expression>> {
55046        if self.match_text_seq(&["WITHIN", "GROUP"]) {
55047            return Ok(Some(Expression::WindowSpec(Box::new(WindowSpec {
55048                partition_by: Vec::new(),
55049                order_by: Vec::new(),
55050                frame: None,
55051            }))));
55052        }
55053        if self.match_text_seq(&["LAST"]) {
55054            // Matched: LAST
55055            return Ok(None);
55056        }
55057        if self.match_text_seq(&["EXCLUDE"]) {
55058            // Matched: EXCLUDE
55059            return Ok(None);
55060        }
55061        Ok(None)
55062    }
55063
55064    /// parse_window_clause - Ported from Python _parse_window_clause
55065    /// Parses WINDOW named_window_definition [, named_window_definition, ...]
55066    #[allow(unused_variables, unused_mut)]
55067    pub fn parse_window_clause(&mut self) -> Result<Option<Expression>> {
55068        if !self.match_token(TokenType::Window) {
55069            return Ok(None);
55070        }
55071
55072        // Parse comma-separated named window definitions
55073        let mut windows = Vec::new();
55074        loop {
55075            // Parse window name
55076            let name = self.parse_identifier()?;
55077            if name.is_none() {
55078                break;
55079            }
55080
55081            // Expect AS
55082            self.expect(TokenType::As)?;
55083
55084            // Parse window specification (parenthesized)
55085            self.expect(TokenType::LParen)?;
55086            let spec = self.parse_window_spec_inner()?;
55087            self.expect(TokenType::RParen)?;
55088
55089            if let (Some(name_expr), Some(spec_expr)) = (name, spec) {
55090                // Create an Alias expression wrapping the spec with the name
55091                let alias_ident = if let Expression::Identifier(id) = name_expr {
55092                    id
55093                } else {
55094                    Identifier::new("window")
55095                };
55096                windows.push(Expression::Alias(Box::new(Alias {
55097                    this: spec_expr,
55098                    alias: alias_ident,
55099                    column_aliases: Vec::new(),
55100                    pre_alias_comments: Vec::new(),
55101                    trailing_comments: Vec::new(),
55102                    inferred_type: None,
55103                })));
55104            }
55105
55106            if !self.match_token(TokenType::Comma) {
55107                break;
55108            }
55109        }
55110
55111        if windows.is_empty() {
55112            Ok(None)
55113        } else {
55114            Ok(Some(Expression::Tuple(Box::new(Tuple {
55115                expressions: windows,
55116            }))))
55117        }
55118    }
55119
55120    /// Parse window spec inner (without parentheses)
55121    fn parse_window_spec_inner(&mut self) -> Result<Option<Expression>> {
55122        // Parse optional base window name (identifier not followed by PARTITION or ORDER or DISTRIBUTE or SORT)
55123        let _base = if (self.check(TokenType::Identifier)
55124            || self.check(TokenType::QuotedIdentifier))
55125            && !self.check(TokenType::Partition)
55126            && !self.check(TokenType::Order)
55127            && !self.check(TokenType::Distribute)
55128            && !self.check(TokenType::Sort)
55129        {
55130            self.parse_identifier()?
55131        } else {
55132            None
55133        };
55134
55135        // Parse PARTITION BY or DISTRIBUTE BY (Hive uses DISTRIBUTE BY in window specs)
55136        let partition_by = if self.match_keywords(&[TokenType::Partition, TokenType::By]) {
55137            self.parse_expression_list()?
55138        } else if self.match_keywords(&[TokenType::Distribute, TokenType::By]) {
55139            // Hive: DISTRIBUTE BY is equivalent to PARTITION BY in window specs
55140            self.parse_expression_list()?
55141        } else {
55142            Vec::new()
55143        };
55144
55145        // Parse ORDER BY or SORT BY (Hive uses SORT BY in window specs)
55146        let order_by = if self.match_token(TokenType::Order) {
55147            self.match_token(TokenType::By);
55148            let mut orders = Vec::new();
55149            loop {
55150                if let Some(ordered) = self.parse_ordered_item()? {
55151                    orders.push(ordered);
55152                } else {
55153                    break;
55154                }
55155                if !self.match_token(TokenType::Comma) {
55156                    break;
55157                }
55158            }
55159            orders
55160        } else if self.match_token(TokenType::Sort) {
55161            // Hive: SORT BY is equivalent to ORDER BY in window specs
55162            self.match_token(TokenType::By);
55163            let mut orders = Vec::new();
55164            loop {
55165                if let Some(ordered) = self.parse_ordered_item()? {
55166                    orders.push(ordered);
55167                } else {
55168                    break;
55169                }
55170                if !self.match_token(TokenType::Comma) {
55171                    break;
55172                }
55173            }
55174            orders
55175        } else {
55176            Vec::new()
55177        };
55178
55179        // Parse frame specification (ROWS/RANGE/GROUPS BETWEEN ... AND ...)
55180        let frame = self.parse_window_frame()?;
55181
55182        Ok(Some(Expression::WindowSpec(Box::new(WindowSpec {
55183            partition_by,
55184            order_by,
55185            frame,
55186        }))))
55187    }
55188
55189    /// parse_window_spec - Implemented from Python _parse_window_spec
55190    #[allow(unused_variables, unused_mut)]
55191    pub fn parse_window_spec(&mut self) -> Result<Option<Expression>> {
55192        if self.match_text_seq(&["UNBOUNDED"]) {
55193            // Matched: UNBOUNDED
55194            return Ok(None);
55195        }
55196        if self.match_text_seq(&["CURRENT", "ROW"]) {
55197            // Matched: CURRENT ROW
55198            return Ok(None);
55199        }
55200        Ok(None)
55201    }
55202
55203    /// parse_with_operator - Parse column with operator class (PostgreSQL)
55204    /// Parses: ordered_expression [WITH operator]
55205    #[allow(unused_variables, unused_mut)]
55206    pub fn parse_with_operator(&mut self) -> Result<Option<Expression>> {
55207        // First parse an ordered expression with optional operator class
55208        let this = if let Some(opclass) = self.parse_opclass()? {
55209            opclass
55210        } else if let Some(ordered) = self.parse_ordered()? {
55211            ordered
55212        } else {
55213            return Ok(None);
55214        };
55215
55216        // Check for WITH operator
55217        if !self.match_token(TokenType::With) {
55218            return Ok(Some(this));
55219        }
55220
55221        // Parse the operator
55222        let op = self.parse_var()?;
55223        let op_str = match op {
55224            Some(Expression::Identifier(id)) => id.name,
55225            Some(Expression::Var(v)) => v.this.clone(),
55226            _ => String::new(),
55227        };
55228
55229        Ok(Some(Expression::WithOperator(Box::new(WithOperator {
55230            this: Box::new(this),
55231            op: op_str,
55232        }))))
55233    }
55234
55235    /// parse_with_property - Implemented from Python _parse_with_property
55236    /// Calls: parse_withjournaltable, parse_withisolatedloading, parse_wrapped_properties
55237    #[allow(unused_variables, unused_mut)]
55238    pub fn parse_with_property(&mut self) -> Result<Option<Expression>> {
55239        if self.match_text_seq(&["(", "SYSTEM_VERSIONING"]) {
55240            return Ok(Some(Expression::WithProcedureOptions(Box::new(
55241                WithProcedureOptions {
55242                    expressions: Vec::new(),
55243                },
55244            ))));
55245        }
55246        if self.match_text_seq(&["JOURNAL"]) {
55247            // Matched: JOURNAL
55248            return Ok(None);
55249        }
55250        if self.match_text_seq(&["DATA"]) {
55251            // Matched: DATA
55252            return Ok(None);
55253        }
55254        Ok(None)
55255    }
55256
55257    /// parse_withdata - Implemented from Python _parse_withdata
55258    #[allow(unused_variables, unused_mut)]
55259    pub fn parse_withdata(&mut self) -> Result<Option<Expression>> {
55260        if self.match_text_seq(&["AND", "STATISTICS"]) {
55261            return Ok(Some(Expression::WithDataProperty(Box::new(
55262                WithDataProperty {
55263                    no: None,
55264                    statistics: None,
55265                },
55266            ))));
55267        }
55268        if self.match_text_seq(&["AND", "NO", "STATISTICS"]) {
55269            // Matched: AND NO STATISTICS
55270            return Ok(None);
55271        }
55272        Ok(None)
55273    }
55274
55275    /// parse_withisolatedloading - Implemented from Python _parse_withisolatedloading
55276    #[allow(unused_variables, unused_mut)]
55277    pub fn parse_withisolatedloading(&mut self) -> Result<Option<Expression>> {
55278        if self.match_text_seq(&["NO"]) {
55279            return Ok(Some(Expression::IsolatedLoadingProperty(Box::new(
55280                IsolatedLoadingProperty {
55281                    no: None,
55282                    concurrent: None,
55283                    target: None,
55284                },
55285            ))));
55286        }
55287        if self.match_text_seq(&["CONCURRENT"]) {
55288            // Matched: CONCURRENT
55289            return Ok(None);
55290        }
55291        Ok(None)
55292    }
55293
55294    /// parse_withjournaltable - Teradata WITH JOURNAL TABLE property
55295    /// Parses: WITH JOURNAL TABLE = table_name
55296    #[allow(unused_variables, unused_mut)]
55297    pub fn parse_withjournaltable(&mut self) -> Result<Option<Expression>> {
55298        // Optionally consume TABLE keyword
55299        self.match_token(TokenType::Table);
55300
55301        // Optionally consume = sign
55302        self.match_token(TokenType::Eq);
55303
55304        // Parse the table reference
55305        let table = self.parse_table_parts()?;
55306        if table.is_none() {
55307            return Ok(None);
55308        }
55309
55310        Ok(Some(Expression::WithJournalTableProperty(Box::new(
55311            WithJournalTableProperty {
55312                this: Box::new(table.unwrap()),
55313            },
55314        ))))
55315    }
55316
55317    /// parse_wrapped - Parses an expression wrapped in parentheses
55318    /// Python: _parse_wrapped(parse_method)
55319    /// This version parses a disjunction (expression) inside parentheses
55320    pub fn parse_wrapped(&mut self) -> Result<Option<Expression>> {
55321        if !self.match_token(TokenType::LParen) {
55322            return Ok(None);
55323        }
55324
55325        let result = self.parse_disjunction()?;
55326        self.match_token(TokenType::RParen);
55327
55328        Ok(result)
55329    }
55330
55331    /// parse_wrapped_csv - Parses comma-separated expressions wrapped in parentheses
55332    /// Python: _parse_wrapped_csv(parse_method)
55333    pub fn parse_wrapped_csv(&mut self) -> Result<Option<Expression>> {
55334        if !self.match_token(TokenType::LParen) {
55335            return Ok(None);
55336        }
55337
55338        let expressions = self.parse_expression_list()?;
55339        self.match_token(TokenType::RParen);
55340
55341        if expressions.is_empty() {
55342            return Ok(None);
55343        }
55344
55345        Ok(Some(Expression::Tuple(Box::new(Tuple { expressions }))))
55346    }
55347
55348    /// parse_wrapped_id_vars - Parses comma-separated identifiers wrapped in parentheses
55349    /// Python: _parse_wrapped_id_vars
55350    pub fn parse_wrapped_id_vars(&mut self) -> Result<Option<Expression>> {
55351        if !self.match_token(TokenType::LParen) {
55352            return Ok(None);
55353        }
55354
55355        let mut identifiers = Vec::new();
55356        loop {
55357            if let Some(id) = self.parse_id_var()? {
55358                identifiers.push(id);
55359            } else {
55360                break;
55361            }
55362            if !self.match_token(TokenType::Comma) {
55363                break;
55364            }
55365        }
55366
55367        self.match_token(TokenType::RParen);
55368
55369        if identifiers.is_empty() {
55370            return Ok(None);
55371        }
55372
55373        Ok(Some(Expression::Tuple(Box::new(Tuple {
55374            expressions: identifiers,
55375        }))))
55376    }
55377
55378    /// parse_wrapped_options - Implemented from Python _parse_wrapped_options
55379    /// Parses space-separated properties wrapped in parentheses (for Snowflake STAGE_FILE_FORMAT, etc.)
55380    /// Format: = (KEY=VALUE KEY2=VALUE2 ...)
55381    pub fn parse_wrapped_options(&mut self) -> Result<Option<Expression>> {
55382        // Match optional = before opening paren
55383        self.match_token(TokenType::Eq);
55384
55385        // Expect opening paren
55386        if !self.match_token(TokenType::LParen) {
55387            return Ok(None);
55388        }
55389
55390        // Parse space-separated properties (no comma required between them)
55391        let mut properties = Vec::new();
55392        while !self.check(TokenType::RParen) && !self.is_at_end() {
55393            // Try to parse a property: KEY=VALUE
55394            if let Some(prop) = self.parse_option_property()? {
55395                properties.push(prop);
55396            } else {
55397                break;
55398            }
55399        }
55400
55401        // Expect closing paren
55402        self.match_token(TokenType::RParen);
55403
55404        if properties.is_empty() {
55405            Ok(None)
55406        } else {
55407            Ok(Some(Expression::Tuple(Box::new(Tuple {
55408                expressions: properties,
55409            }))))
55410        }
55411    }
55412
55413    /// Parse a single option property: KEY=VALUE
55414    /// Handles various value types: identifiers, strings, numbers, nested parens like ('') or (val1, val2)
55415    fn parse_option_property(&mut self) -> Result<Option<Expression>> {
55416        // Save position to retreat if this isn't a property
55417        let index = self.current;
55418
55419        // Parse the key (identifier/column name)
55420        // For Snowflake options, keys are identifiers like TYPE, FIELD_DELIMITER, NULL_IF, etc.
55421        let key = if self.check(TokenType::Identifier)
55422            || self.check(TokenType::Var)
55423            || self
55424                .peek()
55425                .text
55426                .chars()
55427                .all(|c| c.is_ascii_alphanumeric() || c == '_')
55428        {
55429            let name = self.peek().text.clone();
55430            self.skip();
55431            Some(Expression::Var(Box::new(Var { this: name })))
55432        } else {
55433            None
55434        };
55435
55436        let key = match key {
55437            Some(k) => k,
55438            None => {
55439                self.current = index;
55440                return Ok(None);
55441            }
55442        };
55443
55444        // Expect =
55445        if !self.match_token(TokenType::Eq) {
55446            self.current = index;
55447            return Ok(None);
55448        }
55449
55450        // Parse the value - can be:
55451        // - Simple identifier: CSV, SKIP_FILE, BASE64, TRUE, FALSE, CASE_SENSITIVE
55452        // - String literal: '|', '"', 'TZHTZM YYYY-MM-DD HH24:MI:SS.FF9'
55453        // - Number: 5
55454        // - Nested parens for tuple: ('')
55455        let value = if self.check(TokenType::LParen) {
55456            // Parse nested parenthesized value like NULL_IF=('')
55457            self.skip(); // consume (
55458            let mut inner_exprs = Vec::new();
55459            while !self.check(TokenType::RParen) && !self.is_at_end() {
55460                if let Some(expr) = self.parse_primary_for_option()? {
55461                    inner_exprs.push(expr);
55462                }
55463                // Allow comma between nested values
55464                self.match_token(TokenType::Comma);
55465            }
55466            self.match_token(TokenType::RParen);
55467            Expression::Tuple(Box::new(Tuple {
55468                expressions: inner_exprs,
55469            }))
55470        } else if let Some(primary) = self.parse_primary_for_option()? {
55471            primary
55472        } else {
55473            // Fallback: try to parse as a var
55474            let text = self.peek().text.clone();
55475            self.skip();
55476            Expression::Var(Box::new(Var { this: text }))
55477        };
55478
55479        // Return as a Property expression (KEY=VALUE)
55480        Ok(Some(Expression::Property(Box::new(Property {
55481            this: Box::new(key),
55482            value: Some(Box::new(value)),
55483        }))))
55484    }
55485
55486    /// Parse a primary value for option properties
55487    /// Handles strings, numbers, identifiers, TRUE/FALSE
55488    fn parse_primary_for_option(&mut self) -> Result<Option<Expression>> {
55489        // String literal
55490        if self.check(TokenType::String) {
55491            let text = self.peek().text.clone();
55492            self.skip();
55493            return Ok(Some(Expression::Literal(Literal::String(text))));
55494        }
55495
55496        // Number
55497        if self.check(TokenType::Number) {
55498            let text = self.peek().text.clone();
55499            self.skip();
55500            return Ok(Some(Expression::Literal(Literal::Number(text))));
55501        }
55502
55503        // TRUE/FALSE
55504        if self.check(TokenType::True) {
55505            self.skip();
55506            return Ok(Some(Expression::Boolean(BooleanLiteral { value: true })));
55507        }
55508        if self.check(TokenType::False) {
55509            self.skip();
55510            return Ok(Some(Expression::Boolean(BooleanLiteral { value: false })));
55511        }
55512
55513        // Identifier or keyword used as value (CSV, SKIP_FILE, BASE64, etc.)
55514        if self.check(TokenType::Identifier)
55515            || self.check(TokenType::Var)
55516            || (!self.check(TokenType::RParen)
55517                && !self.check(TokenType::Comma)
55518                && !self.check(TokenType::Eq)
55519                && !self.is_at_end())
55520        {
55521            let text = self.peek().text.clone();
55522            // Don't consume if it's a closing paren or could be the next property key followed by =
55523            if self.check(TokenType::RParen) {
55524                return Ok(None);
55525            }
55526            // Check if this is the start of next property (followed by =)
55527            if self.check_next(TokenType::Eq) {
55528                return Ok(None);
55529            }
55530            self.skip();
55531            return Ok(Some(Expression::Var(Box::new(Var { this: text }))));
55532        }
55533
55534        Ok(None)
55535    }
55536
55537    /// parse_options_list - Parses BigQuery-style OPTIONS list: (key=value, key=value, ...)
55538    /// Parses key=value assignments where values can be complex expressions
55539    pub fn parse_options_list(&mut self) -> Result<Vec<Expression>> {
55540        // Expect opening paren
55541        if !self.match_token(TokenType::LParen) {
55542            return Ok(Vec::new());
55543        }
55544
55545        // Parse comma-separated key=value pairs
55546        let mut options = Vec::new();
55547        loop {
55548            // Check for empty OPTIONS () or end of list
55549            if self.check(TokenType::RParen) {
55550                break;
55551            }
55552
55553            // Parse key=value using parse_assignment which handles EQ operations
55554            if let Some(opt) = self.parse_assignment()? {
55555                options.push(opt);
55556            } else {
55557                break;
55558            }
55559
55560            if !self.match_token(TokenType::Comma) {
55561                break;
55562            }
55563        }
55564
55565        // Expect closing paren
55566        self.expect(TokenType::RParen)?;
55567
55568        Ok(options)
55569    }
55570
55571    /// Parse BigQuery PARTITION BY property and return a typed AST node.
55572    fn parse_bigquery_partition_by_property(&mut self) -> Result<Option<Expression>> {
55573        let start = self.current;
55574        let matched_partition = if self.match_token(TokenType::PartitionBy) {
55575            true
55576        } else if self.match_token(TokenType::Partition) {
55577            self.match_token(TokenType::By)
55578        } else {
55579            false
55580        };
55581
55582        if !matched_partition {
55583            self.current = start;
55584            return Ok(None);
55585        }
55586
55587        let mut expressions = Vec::new();
55588        while !self.is_at_end()
55589            && !self.check(TokenType::Cluster)
55590            && !self.check(TokenType::As)
55591            && !self.check(TokenType::Semicolon)
55592            && !self.check(TokenType::RParen)
55593            && !self.check_identifier("OPTIONS")
55594        {
55595            match self.parse_expression() {
55596                Ok(expr) => expressions.push(expr),
55597                Err(_) => {
55598                    // Fall back to generic/raw parsing if typed parsing can't consume this form.
55599                    self.current = start;
55600                    return Ok(None);
55601                }
55602            }
55603
55604            if !self.match_token(TokenType::Comma) {
55605                break;
55606            }
55607        }
55608
55609        if expressions.is_empty() {
55610            self.current = start;
55611            return Ok(None);
55612        }
55613
55614        Ok(Some(Expression::PartitionByProperty(Box::new(
55615            PartitionByProperty { expressions },
55616        ))))
55617    }
55618
55619    /// Parse BigQuery CLUSTER BY property and return a typed AST node.
55620    fn parse_bigquery_cluster_by_property(&mut self) -> Result<Option<Expression>> {
55621        let start = self.current;
55622        if !self.match_keywords(&[TokenType::Cluster, TokenType::By]) {
55623            self.current = start;
55624            return Ok(None);
55625        }
55626
55627        let mut columns = Vec::new();
55628        loop {
55629            if let Some(Expression::Identifier(id)) = self.parse_identifier()? {
55630                columns.push(id);
55631            } else if self.is_identifier_or_keyword_token() {
55632                let name = self.advance().text;
55633                columns.push(Identifier {
55634                    name,
55635                    quoted: false,
55636                    trailing_comments: Vec::new(),
55637                    span: None,
55638                });
55639            } else {
55640                // Fall back to generic/raw parsing if typed parsing can't consume this form.
55641                self.current = start;
55642                return Ok(None);
55643            }
55644
55645            if !self.match_token(TokenType::Comma) {
55646                break;
55647            }
55648        }
55649
55650        if columns.is_empty() {
55651            self.current = start;
55652            return Ok(None);
55653        }
55654
55655        Ok(Some(Expression::ClusterByColumnsProperty(Box::new(
55656            ClusterByColumnsProperty { columns },
55657        ))))
55658    }
55659
55660    /// Parse BigQuery OPTIONS (...) clause into typed entries when possible.
55661    /// Falls back to generic `Properties` when options are not simple key/value assignments.
55662    fn parse_bigquery_options_property(&mut self) -> Result<Option<Expression>> {
55663        let start = self.current;
55664        if !self.match_identifier("OPTIONS") {
55665            self.current = start;
55666            return Ok(None);
55667        }
55668
55669        let options = self.parse_options_list()?;
55670        if options.is_empty() {
55671            return Ok(Some(Expression::OptionsProperty(Box::new(
55672                OptionsProperty {
55673                    entries: Vec::new(),
55674                },
55675            ))));
55676        }
55677
55678        let mut entries = Vec::new();
55679        for option_expr in &options {
55680            let Some(entry) = Self::option_entry_from_expression(option_expr) else {
55681                return Ok(Some(Expression::Properties(Box::new(Properties {
55682                    expressions: options,
55683                }))));
55684            };
55685            entries.push(entry);
55686        }
55687
55688        Ok(Some(Expression::OptionsProperty(Box::new(
55689            OptionsProperty { entries },
55690        ))))
55691    }
55692
55693    fn option_entry_from_expression(expr: &Expression) -> Option<OptionEntry> {
55694        let Expression::Eq(eq) = expr else {
55695            return None;
55696        };
55697
55698        let key = match &eq.left {
55699            Expression::Column(col) if col.table.is_none() => col.name.clone(),
55700            Expression::Identifier(id) => id.clone(),
55701            Expression::Var(var) => Identifier {
55702                name: var.this.clone(),
55703                quoted: false,
55704                trailing_comments: Vec::new(),
55705                span: None,
55706            },
55707            _ => return None,
55708        };
55709
55710        Some(OptionEntry {
55711            key,
55712            value: eq.right.clone(),
55713        })
55714    }
55715
55716    /// parse_environment_list - Parses Databricks ENVIRONMENT list: (dependencies = '...', environment_version = '...')
55717    /// Parses key=value assignments where values can be string literals
55718    pub fn parse_environment_list(&mut self) -> Result<Vec<Expression>> {
55719        // Expect opening paren
55720        if !self.match_token(TokenType::LParen) {
55721            return Ok(Vec::new());
55722        }
55723
55724        // Parse comma-separated key=value pairs
55725        let mut env_items = Vec::new();
55726        loop {
55727            // Check for empty ENVIRONMENT () or end of list
55728            if self.check(TokenType::RParen) {
55729                break;
55730            }
55731
55732            // Parse key=value using parse_assignment which handles EQ operations
55733            if let Some(opt) = self.parse_assignment()? {
55734                env_items.push(opt);
55735            } else {
55736                break;
55737            }
55738
55739            if !self.match_token(TokenType::Comma) {
55740                break;
55741            }
55742        }
55743
55744        // Expect closing paren
55745        self.expect(TokenType::RParen)?;
55746
55747        Ok(env_items)
55748    }
55749
55750    /// parse_wrapped_properties - Ported from Python _parse_wrapped_properties
55751    /// Parses properties wrapped in parentheses
55752    #[allow(unused_variables, unused_mut)]
55753    pub fn parse_wrapped_properties(&mut self) -> Result<Option<Expression>> {
55754        // Parse wrapped list of properties: (prop1, prop2, ...)
55755        if !self.match_token(TokenType::LParen) {
55756            return Ok(None);
55757        }
55758
55759        let mut props = Vec::new();
55760        loop {
55761            if let Some(prop) = self.parse_property()? {
55762                props.push(prop);
55763            }
55764            if !self.match_token(TokenType::Comma) {
55765                break;
55766            }
55767        }
55768
55769        self.match_token(TokenType::RParen);
55770
55771        if props.is_empty() {
55772            return Ok(None);
55773        }
55774
55775        // Return as a Properties expression
55776        Ok(Some(Expression::Properties(Box::new(Properties {
55777            expressions: props,
55778        }))))
55779    }
55780
55781    /// parse_wrapped_select - Ported from Python _parse_wrapped_select
55782    /// Parses wrapped select statements including PIVOT/UNPIVOT and FROM-first syntax
55783    #[allow(unused_variables, unused_mut)]
55784    pub fn parse_wrapped_select(&mut self, table: bool) -> Result<Option<Expression>> {
55785        // Check for PIVOT/UNPIVOT
55786        let is_unpivot = self.check(TokenType::Unpivot);
55787        if self.match_token(TokenType::Pivot) || self.match_token(TokenType::Unpivot) {
55788            // Call simplified pivot parser
55789            return self.parse_simplified_pivot(is_unpivot);
55790        }
55791
55792        // Check for FROM (DuckDB FROM-first syntax)
55793        if self.match_token(TokenType::From) {
55794            // Parse the FROM clause (table reference)
55795            let from_expr = self.parse_table()?;
55796
55797            // Try to parse a full SELECT
55798            let select = self.parse_select_query()?;
55799
55800            if let Some(sel) = select {
55801                // Apply set operations and query modifiers
55802                let with_ops = self.parse_set_operations_with_expr(Some(sel))?;
55803                return Ok(with_ops);
55804            } else if let Some(from_table) = from_expr {
55805                // Create a SELECT * FROM <table>
55806                let mut select_struct = Select::new();
55807                select_struct.expressions = vec![Expression::Star(Star {
55808                    table: None,
55809                    except: None,
55810                    replace: None,
55811                    rename: None,
55812                    trailing_comments: Vec::new(),
55813                    span: None,
55814                })];
55815                select_struct.from = Some(From {
55816                    expressions: vec![from_table],
55817                });
55818                let select_all = Expression::Select(Box::new(select_struct));
55819                let with_ops = self.parse_set_operations_with_expr(Some(select_all))?;
55820                return Ok(with_ops);
55821            }
55822            return Ok(None);
55823        }
55824
55825        // Regular case: parse table or nested select
55826        let this = if table {
55827            self.parse_table()?
55828        } else {
55829            // Parse nested select without set operations
55830            self.parse_select_query()?
55831        };
55832
55833        if this.is_none() {
55834            return Ok(None);
55835        }
55836
55837        // Apply set operations and query modifiers
55838        let with_ops = self.parse_set_operations_with_expr(this)?;
55839        Ok(with_ops)
55840    }
55841
55842    /// Helper for parse_wrapped_select with default table=false
55843    pub fn parse_wrapped_select_default(&mut self) -> Result<Option<Expression>> {
55844        self.parse_wrapped_select(false)
55845    }
55846
55847    /// parse_xml_element - Implemented from Python _parse_xml_element
55848    /// Python: parser.py:6917-6931
55849    /// Parses XMLELEMENT(NAME name [, expr, ...]) or XMLELEMENT(EVALNAME expr [, expr, ...])
55850    #[allow(unused_variables, unused_mut)]
55851    pub fn parse_xml_element(&mut self) -> Result<Option<Expression>> {
55852        let (this, evalname) = if self.match_text_seq(&["EVALNAME"]) {
55853            // EVALNAME - parse expression for dynamic element name
55854            let expr = self.parse_bitwise()?;
55855            (
55856                expr,
55857                Some(Box::new(Expression::Boolean(BooleanLiteral {
55858                    value: true,
55859                }))),
55860            )
55861        } else {
55862            // NAME - parse static element name
55863            self.match_text_seq(&["NAME"]);
55864            let id = self.parse_id_var()?;
55865            (id, None)
55866        };
55867
55868        // Parse optional expressions (comma-separated content/attributes)
55869        let expressions = if self.match_token(TokenType::Comma) {
55870            self.parse_expression_list()?
55871        } else {
55872            Vec::new()
55873        };
55874
55875        match this {
55876            Some(t) => Ok(Some(Expression::XMLElement(Box::new(XMLElement {
55877                this: Box::new(t),
55878                expressions,
55879                evalname,
55880            })))),
55881            None => Ok(None),
55882        }
55883    }
55884
55885    /// parse_xml_namespace - Ported from Python _parse_xml_namespace
55886    /// Parses XML namespace declarations
55887    #[allow(unused_variables, unused_mut)]
55888    pub fn parse_xml_namespace(&mut self) -> Result<Option<Expression>> {
55889        let mut namespaces = Vec::new();
55890
55891        loop {
55892            // Check for DEFAULT namespace
55893            let is_default = self.match_text_seq(&["DEFAULT"]);
55894
55895            // Parse the URI string
55896            let uri = if is_default {
55897                self.parse_string()?
55898            } else {
55899                // Parse URI with optional alias (AS name)
55900                let uri_expr = self.parse_string()?;
55901                if let Some(u) = uri_expr {
55902                    self.parse_alias_with_expr(Some(u))?
55903                } else {
55904                    None
55905                }
55906            };
55907
55908            if let Some(u) = uri {
55909                namespaces.push(u);
55910            }
55911
55912            // Continue if comma
55913            if !self.match_token(TokenType::Comma) {
55914                break;
55915            }
55916        }
55917
55918        if namespaces.is_empty() {
55919            return Ok(None);
55920        }
55921
55922        // Return as a Tuple (list of namespaces)
55923        Ok(Some(Expression::Tuple(Box::new(Tuple {
55924            expressions: namespaces,
55925        }))))
55926    }
55927
55928    /// parse_xml_table - Implemented from Python _parse_xml_table
55929    /// Python: parser.py:6933-6961
55930    /// Parses XMLTABLE(xpath_expr PASSING xml_doc COLUMNS ...)
55931    #[allow(unused_variables, unused_mut)]
55932    pub fn parse_xml_table(&mut self) -> Result<Option<Expression>> {
55933        // Parse optional XMLNAMESPACES clause
55934        let namespaces = if self.match_text_seq(&["XMLNAMESPACES", "("]) {
55935            let ns = self.parse_xml_namespace()?;
55936            self.match_text_seq(&[")", ","]);
55937            ns.map(Box::new)
55938        } else {
55939            None
55940        };
55941
55942        // Parse XPath expression (string)
55943        let this = self.parse_string()?;
55944        if this.is_none() {
55945            return Ok(None);
55946        }
55947
55948        // Parse PASSING clause
55949        let passing = if self.match_text_seq(&["PASSING"]) {
55950            // BY VALUE is optional
55951            self.match_text_seq(&["BY", "VALUE"]);
55952            // Parse comma-separated expressions.
55953            // Oracle XMLTABLE PASSING accepts full expressions (including function calls),
55954            // not just column references.
55955            // We need to stop before COLUMNS, RETURNING, or )
55956            let mut cols = Vec::new();
55957            loop {
55958                // Check for stop keywords before parsing a column
55959                if !self.is_at_end() {
55960                    let next_text = self.peek().text.to_ascii_uppercase();
55961                    if next_text == "COLUMNS" || next_text == "RETURNING" {
55962                        break;
55963                    }
55964                    if self.check(TokenType::RParen) {
55965                        break;
55966                    }
55967                }
55968                if let Some(col) = self.parse_assignment()? {
55969                    cols.push(col);
55970                } else {
55971                    break;
55972                }
55973                if !self.match_token(TokenType::Comma) {
55974                    break;
55975                }
55976            }
55977            if cols.is_empty() {
55978                None
55979            } else {
55980                Some(Box::new(Expression::Tuple(Box::new(Tuple {
55981                    expressions: cols,
55982                }))))
55983            }
55984        } else {
55985            None
55986        };
55987
55988        // Parse optional RETURNING SEQUENCE BY REF
55989        let by_ref = if self.match_text_seq(&["RETURNING", "SEQUENCE", "BY", "REF"]) {
55990            Some(Box::new(Expression::Boolean(BooleanLiteral {
55991                value: true,
55992            })))
55993        } else {
55994            None
55995        };
55996
55997        // Parse COLUMNS clause
55998        let columns = if self.match_text_seq(&["COLUMNS"]) {
55999            let mut cols = Vec::new();
56000            loop {
56001                // Stop if we hit the closing paren
56002                if self.check(TokenType::RParen) {
56003                    break;
56004                }
56005                // Be permissive with leading commas in multiline XMLTABLE COLUMNS lists.
56006                if self.match_token(TokenType::Comma) {
56007                    continue;
56008                }
56009                if let Some(col_def) = self.parse_field_def()? {
56010                    cols.push(col_def);
56011                } else {
56012                    break;
56013                }
56014                if !self.match_token(TokenType::Comma) {
56015                    break;
56016                }
56017            }
56018            cols
56019        } else {
56020            Vec::new()
56021        };
56022
56023        Ok(Some(Expression::XMLTable(Box::new(XMLTable {
56024            this: Box::new(this.unwrap()),
56025            namespaces,
56026            passing,
56027            columns,
56028            by_ref,
56029        }))))
56030    }
56031
56032    /// Parse UNLOAD statement (Athena/Presto/Redshift)
56033    /// UNLOAD (SELECT ...) TO 'location' WITH (options)
56034    fn parse_unload(&mut self) -> Result<Expression> {
56035        // Collect entire statement as a Command
56036        let mut parts = Vec::new();
56037        parts.push(self.advance().text.clone()); // consume UNLOAD
56038        parts.push(" ".to_string()); // space after UNLOAD
56039
56040        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
56041            let token_type = self.peek().token_type;
56042            let token_text = self.peek().text.clone();
56043
56044            // Track string literals
56045            if token_type == TokenType::String {
56046                parts.push(format!("'{}'", token_text.replace('\'', "''")));
56047                self.skip();
56048                // Add space after string unless followed by punctuation
56049                if !self.is_at_end() {
56050                    let next_type = self.peek().token_type;
56051                    if !matches!(
56052                        next_type,
56053                        TokenType::Comma | TokenType::RParen | TokenType::Semicolon
56054                    ) {
56055                        parts.push(" ".to_string());
56056                    }
56057                }
56058                continue;
56059            }
56060
56061            // Handle ARRAY[...] syntax - no space between ARRAY and [
56062            if token_text.eq_ignore_ascii_case("ARRAY")
56063                && self
56064                    .peek_nth(1)
56065                    .is_some_and(|t| t.token_type == TokenType::LBracket)
56066            {
56067                parts.push(token_text);
56068                self.skip();
56069                // Consume [
56070                parts.push("[".to_string());
56071                self.skip();
56072                // Collect until RBracket
56073                while !self.is_at_end() && !self.check(TokenType::RBracket) {
56074                    let inner_type = self.peek().token_type;
56075                    let inner_text = self.peek().text.clone();
56076                    if inner_type == TokenType::String {
56077                        parts.push(format!("'{}'", inner_text.replace('\'', "''")));
56078                    } else {
56079                        parts.push(inner_text);
56080                    }
56081                    self.skip();
56082                    if self.check(TokenType::Comma) {
56083                        parts.push(", ".to_string());
56084                        self.skip();
56085                    }
56086                }
56087                if self.check(TokenType::RBracket) {
56088                    parts.push("]".to_string());
56089                    self.skip();
56090                }
56091                continue;
56092            }
56093
56094            parts.push(token_text);
56095            self.skip();
56096
56097            // Add space after most tokens except punctuation
56098            if !self.is_at_end() {
56099                let next_type = self.peek().token_type;
56100                let no_space_before = matches!(
56101                    next_type,
56102                    TokenType::Comma
56103                        | TokenType::RParen
56104                        | TokenType::RBracket
56105                        | TokenType::Semicolon
56106                        | TokenType::LBracket
56107                );
56108                let no_space_after = matches!(token_type, TokenType::LParen | TokenType::LBracket);
56109                if !no_space_before && !no_space_after {
56110                    parts.push(" ".to_string());
56111                }
56112            }
56113        }
56114
56115        Ok(Expression::Command(Box::new(Command {
56116            this: parts.join(""),
56117        })))
56118    }
56119
56120    /// Parse USING EXTERNAL FUNCTION statement (Athena)
56121    /// USING EXTERNAL FUNCTION name(params) RETURNS type LAMBDA 'arn' SELECT ...
56122    fn parse_using_external_function(&mut self) -> Result<Expression> {
56123        // Record start position
56124        let start_pos = self.peek().span.start;
56125
56126        // Advance through all tokens until end or semicolon
56127        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
56128            self.skip();
56129        }
56130
56131        // Get end position from the last consumed token
56132        let end_pos = if self.current > 0 {
56133            self.tokens[self.current - 1].span.end
56134        } else {
56135            start_pos
56136        };
56137
56138        // Extract exact text from source if available
56139        let command_text = if let Some(ref source) = self.source {
56140            source[start_pos..end_pos].to_string()
56141        } else {
56142            // Fallback: reconstruct from tokens (loses whitespace)
56143            let mut parts = Vec::new();
56144            for i in 0..self.current {
56145                if self.tokens[i].span.start >= start_pos && self.tokens[i].span.end <= end_pos {
56146                    if self.tokens[i].token_type == TokenType::String {
56147                        parts.push(format!("'{}'", self.tokens[i].text.replace('\'', "''")));
56148                    } else {
56149                        parts.push(self.tokens[i].text.clone());
56150                    }
56151                    if i + 1 < self.current {
56152                        parts.push(" ".to_string());
56153                    }
56154                }
56155            }
56156            parts.join("")
56157        };
56158
56159        Ok(Expression::Command(Box::new(Command {
56160            this: command_text,
56161        })))
56162    }
56163}
56164
56165#[cfg(test)]
56166mod tests {
56167    use super::*;
56168    use crate::traversal::ExpressionWalk;
56169
56170    #[test]
56171    fn test_comment_before_limit() {
56172        let sql = "SELECT a FROM b WHERE foo AND bla\n-- comment 3\nLIMIT 10";
56173        let result = Parser::parse_sql(sql).unwrap();
56174        let output = crate::Generator::sql(&result[0]).unwrap();
56175        assert_eq!(
56176            output,
56177            "SELECT a FROM b WHERE foo AND bla LIMIT 10 /* comment 3 */"
56178        );
56179    }
56180
56181    #[test]
56182    fn test_variadic_array_postgres() {
56183        use crate::dialects::DialectType;
56184        use crate::transpile;
56185
56186        // Test: ARRAY[10, -1, 5, 4.4] should parse correctly in Postgres
56187        let sql = "SELECT ARRAY[10, -1, 5, 4.4]";
56188        let result = transpile(sql, DialectType::PostgreSQL, DialectType::PostgreSQL).unwrap();
56189        eprintln!("Array test: {} -> {}", sql, result[0]);
56190
56191        // Test: VARIADIC ARRAY[10, -1, 5, 4.4] in function call
56192        let sql2 = "SELECT MLEAST(VARIADIC ARRAY[10, -1, 5, 4.4])";
56193        let result2 = transpile(sql2, DialectType::PostgreSQL, DialectType::PostgreSQL).unwrap();
56194        eprintln!("VARIADIC test: {} -> {}", sql2, result2[0]);
56195        assert_eq!(result2[0], sql2);
56196    }
56197
56198    #[test]
56199    fn test_parse_simple_select() {
56200        let result = Parser::parse_sql("SELECT 1").unwrap();
56201        assert_eq!(result.len(), 1);
56202        assert!(result[0].is_select());
56203    }
56204
56205    #[test]
56206    fn test_parse_select_from() {
56207        let result = Parser::parse_sql("SELECT a, b FROM t").unwrap();
56208        assert_eq!(result.len(), 1);
56209
56210        let select = result[0].as_select().unwrap();
56211        assert_eq!(select.expressions.len(), 2);
56212        assert!(select.from.is_some());
56213    }
56214
56215    #[test]
56216    fn test_parse_select_where() {
56217        let result = Parser::parse_sql("SELECT * FROM t WHERE x = 1").unwrap();
56218        let select = result[0].as_select().unwrap();
56219        assert!(select.where_clause.is_some());
56220    }
56221
56222    #[test]
56223    fn test_parse_balances_large_and_chain_depth() {
56224        let mut sql = String::from("SELECT 1 WHERE c0 = 0");
56225        for i in 1..4096 {
56226            sql.push_str(&format!(" AND c{i} = {i}"));
56227        }
56228
56229        let result = Parser::parse_sql(&sql).unwrap();
56230        let select = result[0].as_select().unwrap();
56231        let where_clause = select.where_clause.as_ref().expect("WHERE clause missing");
56232        let depth = where_clause.this.tree_depth();
56233        assert!(
56234            depth < 128,
56235            "Expected balanced boolean tree depth, got {}",
56236            depth
56237        );
56238    }
56239
56240    #[test]
56241    fn test_parse_balances_large_or_chain_depth() {
56242        let mut sql = String::from("SELECT 1 WHERE c0 = 0");
56243        for i in 1..4096 {
56244            sql.push_str(&format!(" OR c{i} = {i}"));
56245        }
56246
56247        let result = Parser::parse_sql(&sql).unwrap();
56248        let select = result[0].as_select().unwrap();
56249        let where_clause = select.where_clause.as_ref().expect("WHERE clause missing");
56250        let depth = where_clause.this.tree_depth();
56251        assert!(
56252            depth < 128,
56253            "Expected balanced boolean tree depth, got {}",
56254            depth
56255        );
56256    }
56257
56258    #[test]
56259    fn test_parse_select_join() {
56260        let result = Parser::parse_sql("SELECT * FROM a JOIN b ON a.id = b.id").unwrap();
56261        let select = result[0].as_select().unwrap();
56262        assert_eq!(select.joins.len(), 1);
56263        assert_eq!(select.joins[0].kind, JoinKind::Inner);
56264    }
56265
56266    #[test]
56267    fn test_parse_expression_precedence() {
56268        let result = Parser::parse_sql("SELECT 1 + 2 * 3").unwrap();
56269        let select = result[0].as_select().unwrap();
56270        // Should parse as 1 + (2 * 3) due to precedence
56271        assert!(matches!(select.expressions[0], Expression::Add(_)));
56272    }
56273
56274    #[test]
56275    fn test_parse_function() {
56276        // COUNT(*) is now a typed Count expression
56277        let result = Parser::parse_sql("SELECT COUNT(*)").unwrap();
56278        let select = result[0].as_select().unwrap();
56279        assert!(matches!(select.expressions[0], Expression::Count(_)));
56280
56281        // Unknown functions stay as generic Function
56282        let result = Parser::parse_sql("SELECT MY_CUSTOM_FUNC(name)").unwrap();
56283        let select = result[0].as_select().unwrap();
56284        assert!(matches!(select.expressions[0], Expression::Function(_)));
56285
56286        // Known aggregate functions are now typed
56287        let result = Parser::parse_sql("SELECT SUM(amount)").unwrap();
56288        let select = result[0].as_select().unwrap();
56289        assert!(matches!(select.expressions[0], Expression::Sum(_)));
56290    }
56291
56292    #[test]
56293    fn test_parse_window_function() {
56294        let result =
56295            Parser::parse_sql("SELECT ROW_NUMBER() OVER (PARTITION BY category ORDER BY id)")
56296                .unwrap();
56297        let select = result[0].as_select().unwrap();
56298        assert!(matches!(
56299            select.expressions[0],
56300            Expression::WindowFunction(_)
56301        ));
56302    }
56303
56304    #[test]
56305    fn test_parse_window_function_with_frame() {
56306        let result = Parser::parse_sql("SELECT SUM(amount) OVER (ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)").unwrap();
56307        let select = result[0].as_select().unwrap();
56308        assert!(matches!(
56309            select.expressions[0],
56310            Expression::WindowFunction(_)
56311        ));
56312    }
56313
56314    #[test]
56315    fn test_parse_subscript() {
56316        // Array subscript
56317        let result = Parser::parse_sql("SELECT arr[0]").unwrap();
56318        let select = result[0].as_select().unwrap();
56319        assert!(matches!(select.expressions[0], Expression::Subscript(_)));
56320
56321        // Function result subscript
56322        let result = Parser::parse_sql("SELECT SPLIT(name, ',')[0]").unwrap();
56323        let select = result[0].as_select().unwrap();
56324        assert!(matches!(select.expressions[0], Expression::Subscript(_)));
56325    }
56326
56327    #[test]
56328    fn test_parse_case() {
56329        let result = Parser::parse_sql("SELECT CASE WHEN x = 1 THEN 'a' ELSE 'b' END").unwrap();
56330        let select = result[0].as_select().unwrap();
56331        assert!(matches!(select.expressions[0], Expression::Case(_)));
56332    }
56333
56334    #[test]
56335    fn test_parse_insert() {
56336        let result = Parser::parse_sql("INSERT INTO t (a, b) VALUES (1, 2)").unwrap();
56337        assert!(matches!(result[0], Expression::Insert(_)));
56338    }
56339
56340    #[test]
56341    fn test_parse_template_variable() {
56342        // Test Databricks/Hive ${variable} syntax
56343        let result = Parser::parse_sql("SELECT ${x} FROM ${y} WHERE ${z} > 1").unwrap();
56344        let select = result[0].as_select().unwrap();
56345        // The expression should be a Parameter with DollarBrace style
56346        assert!(
56347            matches!(&select.expressions[0], Expression::Parameter(p) if p.name == Some("x".to_string()))
56348        );
56349        // Check the style is DollarBrace
56350        if let Expression::Parameter(p) = &select.expressions[0] {
56351            assert_eq!(p.style, ParameterStyle::DollarBrace);
56352        }
56353    }
56354
56355    #[test]
56356    fn test_parse_update() {
56357        let result = Parser::parse_sql("UPDATE t SET a = 1 WHERE b = 2").unwrap();
56358        assert!(matches!(result[0], Expression::Update(_)));
56359    }
56360
56361    #[test]
56362    fn test_parse_delete() {
56363        let result = Parser::parse_sql("DELETE FROM t WHERE a = 1").unwrap();
56364        assert!(matches!(result[0], Expression::Delete(_)));
56365    }
56366
56367    // DDL tests
56368    #[test]
56369    fn test_parse_create_table() {
56370        let result = Parser::parse_sql(
56371            "CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(100) NOT NULL)",
56372        )
56373        .unwrap();
56374        assert!(matches!(result[0], Expression::CreateTable(_)));
56375
56376        if let Expression::CreateTable(ct) = &result[0] {
56377            assert_eq!(ct.name.name.name, "users");
56378            assert_eq!(ct.columns.len(), 2);
56379            assert!(ct.columns[0].primary_key);
56380            assert_eq!(ct.columns[1].nullable, Some(false));
56381        }
56382    }
56383
56384    #[test]
56385    fn test_parse_create_table_if_not_exists() {
56386        let result = Parser::parse_sql("CREATE TABLE IF NOT EXISTS t (id INT)").unwrap();
56387        if let Expression::CreateTable(ct) = &result[0] {
56388            assert!(ct.if_not_exists);
56389        }
56390    }
56391
56392    #[test]
56393    fn test_parse_create_temporary_table() {
56394        let result = Parser::parse_sql("CREATE TEMPORARY TABLE t (id INT)").unwrap();
56395        if let Expression::CreateTable(ct) = &result[0] {
56396            assert!(ct.temporary);
56397        }
56398    }
56399
56400    #[test]
56401    fn test_bigquery_create_table_properties_are_typed() {
56402        use crate::DialectType;
56403
56404        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";
56405        let parsed = crate::parse(sql, DialectType::BigQuery).unwrap();
56406
56407        let create = match &parsed[0] {
56408            Expression::CreateTable(ct) => ct,
56409            other => panic!(
56410                "Expected CreateTable, got {:?}",
56411                std::mem::discriminant(other)
56412            ),
56413        };
56414
56415        assert!(
56416            create
56417                .properties
56418                .iter()
56419                .any(|p| matches!(p, Expression::PartitionByProperty(_))),
56420            "Expected typed PARTITION BY property"
56421        );
56422        assert!(
56423            create
56424                .properties
56425                .iter()
56426                .any(|p| matches!(p, Expression::ClusterByColumnsProperty(_))),
56427            "Expected typed CLUSTER BY property"
56428        );
56429        assert!(
56430            create
56431                .properties
56432                .iter()
56433                .any(|p| matches!(p, Expression::OptionsProperty(_))),
56434            "Expected typed OPTIONS property"
56435        );
56436        assert!(
56437            !create
56438                .properties
56439                .iter()
56440                .any(|p| matches!(p, Expression::Raw(_))),
56441            "BigQuery table properties should not fall back to Raw"
56442        );
56443
56444        let options = create
56445            .properties
56446            .iter()
56447            .find_map(|p| match p {
56448                Expression::OptionsProperty(o) => Some(o),
56449                _ => None,
56450            })
56451            .expect("Expected OptionsProperty");
56452        assert_eq!(options.entries.len(), 2);
56453        assert_eq!(options.entries[0].key.name, "description");
56454        assert_eq!(options.entries[1].key.name, "labels");
56455    }
56456
56457    #[test]
56458    fn test_bigquery_create_table_properties_roundtrip() {
56459        use crate::DialectType;
56460
56461        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";
56462        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";
56463        let parsed = crate::parse(sql, DialectType::BigQuery).unwrap();
56464        let generated = crate::generate(&parsed[0], DialectType::BigQuery).unwrap();
56465        assert_eq!(generated, expected);
56466    }
56467
56468    #[test]
56469    fn test_parse_drop_table() {
56470        let result = Parser::parse_sql("DROP TABLE IF EXISTS users CASCADE").unwrap();
56471        assert!(matches!(result[0], Expression::DropTable(_)));
56472
56473        if let Expression::DropTable(dt) = &result[0] {
56474            assert!(dt.if_exists);
56475            assert!(dt.cascade);
56476            assert_eq!(dt.names.len(), 1);
56477        }
56478    }
56479
56480    #[test]
56481    fn test_parse_alter_table_add_column() {
56482        let result = Parser::parse_sql("ALTER TABLE users ADD COLUMN email VARCHAR(255)").unwrap();
56483        assert!(matches!(result[0], Expression::AlterTable(_)));
56484
56485        if let Expression::AlterTable(at) = &result[0] {
56486            assert_eq!(at.actions.len(), 1);
56487            assert!(matches!(at.actions[0], AlterTableAction::AddColumn { .. }));
56488        }
56489    }
56490
56491    #[test]
56492    fn test_parse_alter_table_drop_column() {
56493        let result = Parser::parse_sql("ALTER TABLE users DROP COLUMN email").unwrap();
56494        if let Expression::AlterTable(at) = &result[0] {
56495            assert!(matches!(at.actions[0], AlterTableAction::DropColumn { .. }));
56496        }
56497    }
56498
56499    #[test]
56500    fn test_tsql_alter_table_set_options() {
56501        use crate::{transpile, DialectType};
56502        let tests = vec![
56503            "ALTER TABLE tbl SET (SYSTEM_VERSIONING=OFF)",
56504            "ALTER TABLE tbl SET (FILESTREAM_ON = 'test')",
56505            "ALTER TABLE tbl SET (DATA_DELETION=ON)",
56506            "ALTER TABLE tbl SET (DATA_DELETION=OFF)",
56507            "ALTER TABLE tbl SET (SYSTEM_VERSIONING=ON(HISTORY_TABLE=db.tbl, DATA_CONSISTENCY_CHECK=OFF, HISTORY_RETENTION_PERIOD=5 DAYS))",
56508            "ALTER TABLE tbl SET (SYSTEM_VERSIONING=ON(HISTORY_TABLE=db.tbl, HISTORY_RETENTION_PERIOD=INFINITE))",
56509            "ALTER TABLE tbl SET (DATA_DELETION=ON(FILTER_COLUMN=col, RETENTION_PERIOD=5 MONTHS))",
56510        ];
56511        for sql in tests {
56512            let result = transpile(sql, DialectType::TSQL, DialectType::TSQL);
56513            match result {
56514                Ok(output) => {
56515                    assert_eq!(output[0].trim(), sql, "Identity failed for: {}", sql);
56516                }
56517                Err(e) => {
56518                    panic!("Parse/generate failed for: {} -- {:?}", sql, e);
56519                }
56520            }
56521        }
56522    }
56523
56524    #[test]
56525    fn test_parse_create_index() {
56526        let result = Parser::parse_sql("CREATE UNIQUE INDEX idx_email ON users (email)").unwrap();
56527        assert!(matches!(result[0], Expression::CreateIndex(_)));
56528
56529        if let Expression::CreateIndex(ci) = &result[0] {
56530            assert!(ci.unique);
56531            assert_eq!(ci.name.name, "idx_email");
56532            assert_eq!(ci.table.name.name, "users");
56533            assert_eq!(ci.columns.len(), 1);
56534        }
56535    }
56536
56537    #[test]
56538    fn test_parse_drop_index() {
56539        let result = Parser::parse_sql("DROP INDEX IF EXISTS idx_email ON users").unwrap();
56540        assert!(matches!(result[0], Expression::DropIndex(_)));
56541
56542        if let Expression::DropIndex(di) = &result[0] {
56543            assert!(di.if_exists);
56544            assert!(di.table.is_some());
56545        }
56546    }
56547
56548    #[test]
56549    fn test_parse_create_view() {
56550        let result =
56551            Parser::parse_sql("CREATE VIEW active_users AS SELECT * FROM users WHERE active = 1")
56552                .unwrap();
56553        assert!(matches!(result[0], Expression::CreateView(_)));
56554    }
56555
56556    #[test]
56557    fn test_parse_create_materialized_view() {
56558        let result =
56559            Parser::parse_sql("CREATE MATERIALIZED VIEW stats AS SELECT COUNT(*) FROM users")
56560                .unwrap();
56561        if let Expression::CreateView(cv) = &result[0] {
56562            assert!(cv.materialized);
56563        }
56564    }
56565
56566    #[test]
56567    fn test_hive_stored_by() {
56568        use crate::{transpile, DialectType};
56569        let sql = "CREATE EXTERNAL TABLE X (y INT) STORED BY 'x'";
56570        let result = transpile(sql, DialectType::Hive, DialectType::Hive);
56571        match result {
56572            Ok(output) => {
56573                assert_eq!(output[0].trim(), sql, "Identity failed for: {}", sql);
56574            }
56575            Err(e) => {
56576                panic!("Parse/generate failed for: {} -- {:?}", sql, e);
56577            }
56578        }
56579    }
56580
56581    #[test]
56582    fn test_hive_row_format_serde() {
56583        use crate::{transpile, DialectType};
56584
56585        // Test various Hive CREATE TABLE syntax
56586        let test_cases = vec![
56587            (
56588                "CREATE TABLE my_table (a7 ARRAY<DATE>)",
56589                "CREATE TABLE my_table (a7 ARRAY<DATE>)",
56590            ),
56591            (
56592                "CREATE EXTERNAL TABLE my_table (x INT) ROW FORMAT SERDE 'a'",
56593                "CREATE EXTERNAL TABLE my_table (x INT) ROW FORMAT SERDE 'a'",
56594            ),
56595            (
56596                "CREATE EXTERNAL TABLE my_table (x INT) STORED AS INPUTFORMAT 'b' OUTPUTFORMAT 'c'",
56597                "CREATE EXTERNAL TABLE my_table (x INT) STORED AS INPUTFORMAT 'b' OUTPUTFORMAT 'c'",
56598            ),
56599            (
56600                "CREATE EXTERNAL TABLE my_table (x INT) LOCATION 'd'",
56601                "CREATE EXTERNAL TABLE my_table (x INT) LOCATION 'd'",
56602            ),
56603            (
56604                "CREATE EXTERNAL TABLE my_table (x INT) TBLPROPERTIES ('e'='f')",
56605                "CREATE EXTERNAL TABLE my_table (x INT) TBLPROPERTIES ('e'='f')",
56606            ),
56607            (
56608                "CREATE EXTERNAL TABLE X (y INT) STORED BY 'x'",
56609                "CREATE EXTERNAL TABLE X (y INT) STORED BY 'x'",
56610            ),
56611        ];
56612
56613        for (sql, expected) in &test_cases {
56614            let result = transpile(sql, DialectType::Hive, DialectType::Hive);
56615            match result {
56616                Ok(output) => {
56617                    assert_eq!(output[0].trim(), *expected, "Identity failed for: {}", sql);
56618                }
56619                Err(e) => {
56620                    panic!("Parse/generate failed for: {} -- {:?}", sql, e);
56621                }
56622            }
56623        }
56624
56625        // Test full case with all Hive table properties
56626        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')";
56627        let result = transpile(sql, DialectType::Hive, DialectType::Hive);
56628        match result {
56629            Ok(output) => {
56630                assert_eq!(output[0].trim(), sql, "Identity failed for: {}", sql);
56631            }
56632            Err(e) => {
56633                panic!("Parse/generate failed for: {} -- {:?}", sql, e);
56634            }
56635        }
56636    }
56637
56638    #[test]
56639    fn test_parse_drop_view() {
56640        let result = Parser::parse_sql("DROP VIEW IF EXISTS active_users").unwrap();
56641        assert!(matches!(result[0], Expression::DropView(_)));
56642    }
56643
56644    #[test]
56645    fn test_parse_truncate() {
56646        let result = Parser::parse_sql("TRUNCATE TABLE users CASCADE").unwrap();
56647        assert!(matches!(result[0], Expression::Truncate(_)));
56648
56649        if let Expression::Truncate(tr) = &result[0] {
56650            assert!(tr.cascade);
56651        }
56652    }
56653
56654    // Tests for typed aggregate functions
56655    #[test]
56656    fn test_parse_typed_aggregates() {
56657        // COUNT with DISTINCT
56658        let result = Parser::parse_sql("SELECT COUNT(DISTINCT user_id)").unwrap();
56659        let select = result[0].as_select().unwrap();
56660        if let Expression::Count(c) = &select.expressions[0] {
56661            assert!(c.distinct);
56662            assert!(!c.star);
56663        } else {
56664            panic!("Expected Count expression");
56665        }
56666
56667        // AVG
56668        let result = Parser::parse_sql("SELECT AVG(price)").unwrap();
56669        let select = result[0].as_select().unwrap();
56670        assert!(matches!(select.expressions[0], Expression::Avg(_)));
56671
56672        // MIN/MAX
56673        let result = Parser::parse_sql("SELECT MIN(a), MAX(b)").unwrap();
56674        let select = result[0].as_select().unwrap();
56675        assert!(matches!(select.expressions[0], Expression::Min(_)));
56676        assert!(matches!(select.expressions[1], Expression::Max(_)));
56677
56678        // STDDEV/VARIANCE
56679        let result = Parser::parse_sql("SELECT STDDEV(x), VARIANCE(y)").unwrap();
56680        let select = result[0].as_select().unwrap();
56681        assert!(matches!(select.expressions[0], Expression::Stddev(_)));
56682        assert!(matches!(select.expressions[1], Expression::Variance(_)));
56683    }
56684
56685    #[test]
56686    fn test_parse_typed_window_functions() {
56687        // ROW_NUMBER
56688        let result = Parser::parse_sql("SELECT ROW_NUMBER() OVER (ORDER BY id)").unwrap();
56689        let select = result[0].as_select().unwrap();
56690        if let Expression::WindowFunction(wf) = &select.expressions[0] {
56691            assert!(matches!(wf.this, Expression::RowNumber(_)));
56692        } else {
56693            panic!("Expected WindowFunction");
56694        }
56695
56696        // RANK and DENSE_RANK
56697        let result = Parser::parse_sql("SELECT RANK() OVER (), DENSE_RANK() OVER ()").unwrap();
56698        let select = result[0].as_select().unwrap();
56699        if let Expression::WindowFunction(wf) = &select.expressions[0] {
56700            assert!(matches!(wf.this, Expression::Rank(_)));
56701        }
56702        if let Expression::WindowFunction(wf) = &select.expressions[1] {
56703            assert!(matches!(wf.this, Expression::DenseRank(_)));
56704        }
56705
56706        // LEAD/LAG
56707        let result = Parser::parse_sql("SELECT LEAD(val, 1, 0) OVER (ORDER BY id)").unwrap();
56708        let select = result[0].as_select().unwrap();
56709        if let Expression::WindowFunction(wf) = &select.expressions[0] {
56710            if let Expression::Lead(f) = &wf.this {
56711                assert!(f.offset.is_some());
56712                assert!(f.default.is_some());
56713            } else {
56714                panic!("Expected Lead");
56715            }
56716        }
56717
56718        // NTILE
56719        let result = Parser::parse_sql("SELECT NTILE(4) OVER (ORDER BY score)").unwrap();
56720        let select = result[0].as_select().unwrap();
56721        if let Expression::WindowFunction(wf) = &select.expressions[0] {
56722            assert!(matches!(wf.this, Expression::NTile(_)));
56723        }
56724    }
56725
56726    #[test]
56727    fn test_parse_string_functions() {
56728        // CONTAINS, STARTS_WITH, ENDS_WITH
56729        let result = Parser::parse_sql("SELECT CONTAINS(name, 'test')").unwrap();
56730        let select = result[0].as_select().unwrap();
56731        assert!(matches!(select.expressions[0], Expression::Contains(_)));
56732
56733        let result = Parser::parse_sql("SELECT STARTS_WITH(name, 'A')").unwrap();
56734        let select = result[0].as_select().unwrap();
56735        assert!(matches!(select.expressions[0], Expression::StartsWith(_)));
56736
56737        let result = Parser::parse_sql("SELECT ENDS_WITH(name, 'z')").unwrap();
56738        let select = result[0].as_select().unwrap();
56739        assert!(matches!(select.expressions[0], Expression::EndsWith(_)));
56740    }
56741
56742    #[test]
56743    fn test_parse_math_functions() {
56744        // MOD function
56745        let result = Parser::parse_sql("SELECT MOD(10, 3)").unwrap();
56746        let select = result[0].as_select().unwrap();
56747        assert!(matches!(select.expressions[0], Expression::ModFunc(_)));
56748
56749        // RANDOM and RAND
56750        let result = Parser::parse_sql("SELECT RANDOM()").unwrap();
56751        let select = result[0].as_select().unwrap();
56752        assert!(matches!(select.expressions[0], Expression::Random(_)));
56753
56754        let result = Parser::parse_sql("SELECT RAND(42)").unwrap();
56755        let select = result[0].as_select().unwrap();
56756        assert!(matches!(select.expressions[0], Expression::Rand(_)));
56757
56758        // Trigonometric functions
56759        let result = Parser::parse_sql("SELECT SIN(x), COS(x), TAN(x)").unwrap();
56760        let select = result[0].as_select().unwrap();
56761        assert!(matches!(select.expressions[0], Expression::Sin(_)));
56762        assert!(matches!(select.expressions[1], Expression::Cos(_)));
56763        assert!(matches!(select.expressions[2], Expression::Tan(_)));
56764    }
56765
56766    #[test]
56767    fn test_parse_date_functions() {
56768        // Date part extraction functions
56769        let result =
56770            Parser::parse_sql("SELECT YEAR(date_col), MONTH(date_col), DAY(date_col)").unwrap();
56771        let select = result[0].as_select().unwrap();
56772        assert!(matches!(select.expressions[0], Expression::Year(_)));
56773        assert!(matches!(select.expressions[1], Expression::Month(_)));
56774        assert!(matches!(select.expressions[2], Expression::Day(_)));
56775
56776        // EPOCH and EPOCH_MS
56777        let result = Parser::parse_sql("SELECT EPOCH(ts), EPOCH_MS(ts)").unwrap();
56778        let select = result[0].as_select().unwrap();
56779        assert!(matches!(select.expressions[0], Expression::Epoch(_)));
56780        assert!(matches!(select.expressions[1], Expression::EpochMs(_)));
56781    }
56782
56783    #[test]
56784    fn test_parse_array_functions() {
56785        // ARRAY_LENGTH
56786        let result = Parser::parse_sql("SELECT ARRAY_LENGTH(arr)").unwrap();
56787        let select = result[0].as_select().unwrap();
56788        assert!(matches!(select.expressions[0], Expression::ArrayLength(_)));
56789
56790        // ARRAY_CONTAINS
56791        let result = Parser::parse_sql("SELECT ARRAY_CONTAINS(arr, 1)").unwrap();
56792        let select = result[0].as_select().unwrap();
56793        assert!(matches!(
56794            select.expressions[0],
56795            Expression::ArrayContains(_)
56796        ));
56797
56798        // EXPLODE
56799        let result = Parser::parse_sql("SELECT EXPLODE(arr)").unwrap();
56800        let select = result[0].as_select().unwrap();
56801        assert!(matches!(select.expressions[0], Expression::Explode(_)));
56802    }
56803
56804    #[test]
56805    fn test_parse_json_functions() {
56806        // JSON_EXTRACT
56807        let result = Parser::parse_sql("SELECT JSON_EXTRACT(data, '$.name')").unwrap();
56808        let select = result[0].as_select().unwrap();
56809        assert!(matches!(select.expressions[0], Expression::JsonExtract(_)));
56810
56811        // JSON_ARRAY_LENGTH
56812        let result = Parser::parse_sql("SELECT JSON_ARRAY_LENGTH(arr)").unwrap();
56813        let select = result[0].as_select().unwrap();
56814        assert!(matches!(
56815            select.expressions[0],
56816            Expression::JsonArrayLength(_)
56817        ));
56818
56819        // TO_JSON and PARSE_JSON
56820        let result = Parser::parse_sql("SELECT TO_JSON(obj), PARSE_JSON(str)").unwrap();
56821        let select = result[0].as_select().unwrap();
56822        assert!(matches!(select.expressions[0], Expression::ToJson(_)));
56823        assert!(matches!(select.expressions[1], Expression::ParseJson(_)));
56824
56825        // JSON literal: JSON '"foo"' -> ParseJson
56826        let result = Parser::parse_sql("SELECT JSON '\"foo\"'").unwrap();
56827        let select = result[0].as_select().unwrap();
56828        assert!(
56829            matches!(select.expressions[0], Expression::ParseJson(_)),
56830            "Expected ParseJson, got: {:?}",
56831            select.expressions[0]
56832        );
56833    }
56834
56835    #[test]
56836    fn test_parse_map_functions() {
56837        // MAP_KEYS and MAP_VALUES
56838        let result = Parser::parse_sql("SELECT MAP_KEYS(m), MAP_VALUES(m)").unwrap();
56839        let select = result[0].as_select().unwrap();
56840        assert!(matches!(select.expressions[0], Expression::MapKeys(_)));
56841        assert!(matches!(select.expressions[1], Expression::MapValues(_)));
56842
56843        // ELEMENT_AT
56844        let result = Parser::parse_sql("SELECT ELEMENT_AT(m, 'key')").unwrap();
56845        let select = result[0].as_select().unwrap();
56846        assert!(matches!(select.expressions[0], Expression::ElementAt(_)));
56847    }
56848
56849    #[test]
56850    fn test_parse_date_literals() {
56851        // DATE literal (generic mode normalizes to CAST)
56852        let result = Parser::parse_sql("SELECT DATE '2024-01-15'").unwrap();
56853        let select = result[0].as_select().unwrap();
56854        match &select.expressions[0] {
56855            Expression::Cast(cast) => {
56856                match &cast.this {
56857                    Expression::Literal(Literal::String(s)) => assert_eq!(s, "2024-01-15"),
56858                    other => panic!("Expected String literal in Cast, got {:?}", other),
56859                }
56860                assert!(matches!(cast.to, DataType::Date));
56861            }
56862            other => panic!("Expected Cast expression, got {:?}", other),
56863        }
56864
56865        // TIME literal
56866        let result = Parser::parse_sql("SELECT TIME '10:30:00'").unwrap();
56867        let select = result[0].as_select().unwrap();
56868        match &select.expressions[0] {
56869            Expression::Literal(Literal::Time(t)) => {
56870                assert_eq!(t, "10:30:00");
56871            }
56872            _ => panic!("Expected Time literal"),
56873        }
56874
56875        // TIMESTAMP literal -> CAST in generic mode
56876        let result = Parser::parse_sql("SELECT TIMESTAMP '2024-01-15 10:30:00'").unwrap();
56877        let select = result[0].as_select().unwrap();
56878        match &select.expressions[0] {
56879            Expression::Cast(cast) => {
56880                match &cast.this {
56881                    Expression::Literal(Literal::String(s)) => assert_eq!(s, "2024-01-15 10:30:00"),
56882                    other => panic!("Expected String literal inside Cast, got {:?}", other),
56883                }
56884                assert!(matches!(
56885                    &cast.to,
56886                    DataType::Timestamp {
56887                        precision: None,
56888                        timezone: false
56889                    }
56890                ));
56891            }
56892            _ => panic!("Expected Cast expression for TIMESTAMP literal"),
56893        }
56894    }
56895
56896    #[test]
56897    fn test_parse_star_exclude() {
56898        // EXCLUDE with multiple columns
56899        let result = Parser::parse_sql("SELECT * EXCLUDE (col1, col2) FROM t").unwrap();
56900        let select = result[0].as_select().unwrap();
56901        if let Expression::Star(star) = &select.expressions[0] {
56902            assert!(star.except.is_some());
56903            let except = star.except.as_ref().unwrap();
56904            assert_eq!(except.len(), 2);
56905            assert_eq!(except[0].name, "col1");
56906            assert_eq!(except[1].name, "col2");
56907        } else {
56908            panic!("Expected Star expression");
56909        }
56910
56911        // EXCEPT (BigQuery syntax)
56912        let result = Parser::parse_sql("SELECT * EXCEPT (id, created_at) FROM t").unwrap();
56913        let select = result[0].as_select().unwrap();
56914        if let Expression::Star(star) = &select.expressions[0] {
56915            assert!(star.except.is_some());
56916        } else {
56917            panic!("Expected Star expression");
56918        }
56919
56920        // table.* with EXCLUDE
56921        let result = Parser::parse_sql("SELECT t.* EXCLUDE (col1) FROM t").unwrap();
56922        let select = result[0].as_select().unwrap();
56923        if let Expression::Star(star) = &select.expressions[0] {
56924            assert!(star.table.is_some());
56925            assert_eq!(star.table.as_ref().unwrap().name, "t");
56926            assert!(star.except.is_some());
56927        } else {
56928            panic!("Expected Star expression");
56929        }
56930    }
56931
56932    #[test]
56933    fn test_parse_star_replace() {
56934        // REPLACE with single expression
56935        let result = Parser::parse_sql("SELECT * REPLACE (UPPER(name) AS name) FROM t").unwrap();
56936        let select = result[0].as_select().unwrap();
56937        if let Expression::Star(star) = &select.expressions[0] {
56938            assert!(star.replace.is_some());
56939            let replace = star.replace.as_ref().unwrap();
56940            assert_eq!(replace.len(), 1);
56941            assert_eq!(replace[0].alias.name, "name");
56942        } else {
56943            panic!("Expected Star expression");
56944        }
56945
56946        // REPLACE with multiple expressions
56947        let result = Parser::parse_sql("SELECT * REPLACE (a + 1 AS a, b * 2 AS b) FROM t").unwrap();
56948        let select = result[0].as_select().unwrap();
56949        if let Expression::Star(star) = &select.expressions[0] {
56950            let replace = star.replace.as_ref().unwrap();
56951            assert_eq!(replace.len(), 2);
56952        } else {
56953            panic!("Expected Star expression");
56954        }
56955    }
56956
56957    #[test]
56958    fn test_parse_star_rename() {
56959        // RENAME with multiple columns
56960        let result =
56961            Parser::parse_sql("SELECT * RENAME (old_col AS new_col, x AS y) FROM t").unwrap();
56962        let select = result[0].as_select().unwrap();
56963        if let Expression::Star(star) = &select.expressions[0] {
56964            assert!(star.rename.is_some());
56965            let rename = star.rename.as_ref().unwrap();
56966            assert_eq!(rename.len(), 2);
56967            assert_eq!(rename[0].0.name, "old_col");
56968            assert_eq!(rename[0].1.name, "new_col");
56969        } else {
56970            panic!("Expected Star expression");
56971        }
56972    }
56973
56974    #[test]
56975    fn test_parse_star_combined() {
56976        // EXCLUDE + REPLACE combined
56977        let result =
56978            Parser::parse_sql("SELECT * EXCLUDE (id) REPLACE (name || '!' AS name) FROM t")
56979                .unwrap();
56980        let select = result[0].as_select().unwrap();
56981        if let Expression::Star(star) = &select.expressions[0] {
56982            assert!(star.except.is_some());
56983            assert!(star.replace.is_some());
56984        } else {
56985            panic!("Expected Star expression");
56986        }
56987    }
56988
56989    #[test]
56990    fn test_parse_spatial_types() {
56991        // GEOMETRY with subtype and SRID (PostgreSQL syntax)
56992        let result = Parser::parse_sql("CREATE TABLE t (geom GEOMETRY(Point, 4326))").unwrap();
56993        if let Expression::CreateTable(ct) = &result[0] {
56994            assert_eq!(ct.columns.len(), 1);
56995            match &ct.columns[0].data_type {
56996                DataType::Geometry { subtype, srid } => {
56997                    assert_eq!(subtype.as_deref(), Some("POINT"));
56998                    assert_eq!(*srid, Some(4326));
56999                }
57000                _ => panic!("Expected Geometry type"),
57001            }
57002        }
57003
57004        // GEOGRAPHY without parameters
57005        let result = Parser::parse_sql("CREATE TABLE t (loc GEOGRAPHY)").unwrap();
57006        if let Expression::CreateTable(ct) = &result[0] {
57007            match &ct.columns[0].data_type {
57008                DataType::Geography { subtype, srid } => {
57009                    assert!(subtype.is_none());
57010                    assert!(srid.is_none());
57011                }
57012                _ => panic!("Expected Geography type"),
57013            }
57014        }
57015
57016        // GEOMETRY subtype only (no SRID)
57017        let result = Parser::parse_sql("CREATE TABLE t (geom GEOMETRY(LineString))").unwrap();
57018        if let Expression::CreateTable(ct) = &result[0] {
57019            match &ct.columns[0].data_type {
57020                DataType::Geometry { subtype, srid } => {
57021                    assert_eq!(subtype.as_deref(), Some("LINESTRING"));
57022                    assert!(srid.is_none());
57023                }
57024                _ => panic!("Expected Geometry type"),
57025            }
57026        }
57027
57028        // Simple POINT type (MySQL-style without SRID)
57029        let result = Parser::parse_sql("CREATE TABLE t (pt POINT)").unwrap();
57030        if let Expression::CreateTable(ct) = &result[0] {
57031            match &ct.columns[0].data_type {
57032                DataType::Geometry { subtype, srid } => {
57033                    assert_eq!(subtype.as_deref(), Some("POINT"));
57034                    assert!(srid.is_none());
57035                }
57036                _ => panic!("Expected Geometry type"),
57037            }
57038        }
57039    }
57040
57041    #[test]
57042    fn test_parse_duckdb_pivot_simple() {
57043        let sql = "PIVOT Cities ON Year USING SUM(Population)";
57044        let result = Parser::parse_sql(sql);
57045        assert!(
57046            result.is_ok(),
57047            "Failed to parse: {} - {:?}",
57048            sql,
57049            result.err()
57050        );
57051        let stmts = result.unwrap();
57052        assert_eq!(
57053            stmts.len(),
57054            1,
57055            "Expected 1 statement, got {}: {:?}",
57056            stmts.len(),
57057            stmts
57058        );
57059        match &stmts[0] {
57060            Expression::Pivot(p) => {
57061                assert!(!p.unpivot);
57062                assert!(!p.expressions.is_empty(), "Should have ON expressions");
57063                assert!(!p.using.is_empty(), "Should have USING expressions");
57064            }
57065            other => panic!("Expected Pivot, got {:?}", other),
57066        }
57067    }
57068
57069    #[test]
57070    fn test_parse_duckdb_pivot_with_group_by() {
57071        let sql = "PIVOT Cities ON Year USING SUM(Population) GROUP BY Country";
57072        let result = Parser::parse_sql(sql);
57073        assert!(
57074            result.is_ok(),
57075            "Failed to parse: {} - {:?}",
57076            sql,
57077            result.err()
57078        );
57079    }
57080
57081    #[test]
57082    fn test_parse_duckdb_unpivot() {
57083        let sql = "UNPIVOT monthly_sales ON jan, feb, mar INTO NAME month VALUE sales";
57084        let result = Parser::parse_sql(sql);
57085        assert!(
57086            result.is_ok(),
57087            "Failed to parse: {} - {:?}",
57088            sql,
57089            result.err()
57090        );
57091    }
57092
57093    #[test]
57094    fn test_parse_standard_pivot_in_from() {
57095        let sql = "SELECT * FROM cities PIVOT(SUM(population) FOR year IN (2000, 2010, 2020))";
57096        let result = Parser::parse_sql(sql);
57097        assert!(
57098            result.is_ok(),
57099            "Failed to parse: {} - {:?}",
57100            sql,
57101            result.err()
57102        );
57103    }
57104
57105    fn assert_pivot_roundtrip(sql: &str) {
57106        let parsed = crate::parse(sql, crate::DialectType::DuckDB);
57107        assert!(
57108            parsed.is_ok(),
57109            "Failed to parse: {} - {:?}",
57110            sql,
57111            parsed.err()
57112        );
57113        let stmts = parsed.unwrap();
57114        assert_eq!(stmts.len(), 1, "Expected 1 statement for: {}", sql);
57115        let generated = crate::generate(&stmts[0], crate::DialectType::DuckDB);
57116        assert!(
57117            generated.is_ok(),
57118            "Failed to generate: {} - {:?}",
57119            sql,
57120            generated.err()
57121        );
57122        let result = generated.unwrap();
57123        assert_eq!(result.trim(), sql, "Round-trip mismatch for: {}", sql);
57124    }
57125
57126    fn assert_pivot_roundtrip_bq(sql: &str) {
57127        let parsed = crate::parse(sql, crate::DialectType::BigQuery);
57128        assert!(
57129            parsed.is_ok(),
57130            "Failed to parse: {} - {:?}",
57131            sql,
57132            parsed.err()
57133        );
57134        let stmts = parsed.unwrap();
57135        assert_eq!(stmts.len(), 1, "Expected 1 statement for: {}", sql);
57136        let generated = crate::generate(&stmts[0], crate::DialectType::BigQuery);
57137        assert!(
57138            generated.is_ok(),
57139            "Failed to generate: {} - {:?}",
57140            sql,
57141            generated.err()
57142        );
57143        let result = generated.unwrap();
57144        assert_eq!(result.trim(), sql, "Round-trip mismatch for: {}", sql);
57145    }
57146
57147    #[test]
57148    fn test_pivot_roundtrip_duckdb_simple() {
57149        assert_pivot_roundtrip("PIVOT Cities ON Year USING SUM(Population)");
57150    }
57151
57152    #[test]
57153    fn test_pivot_roundtrip_duckdb_group_by() {
57154        assert_pivot_roundtrip("PIVOT Cities ON Year USING SUM(Population) GROUP BY Country");
57155    }
57156
57157    #[test]
57158    fn test_pivot_roundtrip_duckdb_in_clause() {
57159        assert_pivot_roundtrip(
57160            "PIVOT Cities ON Year IN (2000, 2010) USING SUM(Population) GROUP BY Country",
57161        );
57162    }
57163
57164    #[test]
57165    fn test_pivot_roundtrip_duckdb_multiple_using() {
57166        assert_pivot_roundtrip("PIVOT Cities ON Year USING SUM(Population) AS total, MAX(Population) AS max GROUP BY Country");
57167    }
57168
57169    #[test]
57170    fn test_pivot_roundtrip_duckdb_multiple_on() {
57171        assert_pivot_roundtrip("PIVOT Cities ON Country, Name USING SUM(Population)");
57172    }
57173
57174    #[test]
57175    fn test_pivot_roundtrip_duckdb_concat_on() {
57176        assert_pivot_roundtrip("PIVOT Cities ON Country || '_' || Name USING SUM(Population)");
57177    }
57178
57179    #[test]
57180    fn test_pivot_roundtrip_duckdb_multiple_group_by() {
57181        assert_pivot_roundtrip("PIVOT Cities ON Year USING SUM(Population) GROUP BY Country, Name");
57182    }
57183
57184    #[test]
57185    fn test_pivot_roundtrip_duckdb_first() {
57186        assert_pivot_roundtrip("PIVOT Cities ON Year USING FIRST(Population)");
57187    }
57188
57189    #[test]
57190    fn test_unpivot_roundtrip_duckdb_basic() {
57191        assert_pivot_roundtrip(
57192            "UNPIVOT monthly_sales ON jan, feb, mar, apr, may, jun INTO NAME month VALUE sales",
57193        );
57194    }
57195
57196    #[test]
57197    fn test_unpivot_roundtrip_duckdb_subquery() {
57198        assert_pivot_roundtrip("UNPIVOT (SELECT 1 AS col1, 2 AS col2) ON foo, bar");
57199    }
57200
57201    #[test]
57202    fn test_pivot_roundtrip_duckdb_cte() {
57203        assert_pivot_roundtrip("WITH pivot_alias AS (PIVOT Cities ON Year USING SUM(Population) GROUP BY Country) SELECT * FROM pivot_alias");
57204    }
57205
57206    #[test]
57207    fn test_pivot_roundtrip_duckdb_subquery() {
57208        assert_pivot_roundtrip("SELECT * FROM (PIVOT Cities ON Year USING SUM(Population) GROUP BY Country) AS pivot_alias");
57209    }
57210
57211    #[test]
57212    fn test_pivot_roundtrip_standard_from() {
57213        assert_pivot_roundtrip("SELECT * FROM cities PIVOT(SUM(population) FOR year IN (2000, 2010, 2020) GROUP BY country)");
57214    }
57215
57216    #[test]
57217    fn test_pivot_roundtrip_standard_bare_in() {
57218        assert_pivot_roundtrip("SELECT * FROM t PIVOT(SUM(y) FOR foo IN y_enum)");
57219    }
57220
57221    #[test]
57222    fn test_unpivot_roundtrip_bigquery() {
57223        assert_pivot_roundtrip_bq("SELECT * FROM q UNPIVOT(values FOR quarter IN (b, c))");
57224    }
57225
57226    #[test]
57227    fn test_pivot_roundtrip_bigquery_aliases() {
57228        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))");
57229    }
57230
57231    #[test]
57232    fn test_unpivot_roundtrip_bigquery_parens() {
57233        assert_pivot_roundtrip_bq(
57234            "SELECT * FROM (SELECT * FROM `t`) AS a UNPIVOT((c) FOR c_name IN (v1, v2))",
57235        );
57236    }
57237
57238    #[test]
57239    fn test_pivot_roundtrip_bigquery_multi_agg() {
57240        // Note: BigQuery fixture expects implicit aliases to become explicit AS
57241        let sql = "SELECT * FROM (SELECT a, b, c FROM test) PIVOT(SUM(b) AS d, COUNT(*) AS e FOR c IN ('x', 'y'))";
57242        assert_pivot_roundtrip_bq(sql);
57243    }
57244
57245    // Additional fixture tests for UNPIVOT with COLUMNS and grouped ON
57246    #[test]
57247    fn test_unpivot_roundtrip_duckdb_columns_exclude() {
57248        assert_pivot_roundtrip(
57249            "UNPIVOT monthly_sales ON COLUMNS(* EXCLUDE (empid, dept)) INTO NAME month VALUE sales",
57250        );
57251    }
57252
57253    #[test]
57254    fn test_unpivot_roundtrip_duckdb_grouped_columns() {
57255        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");
57256    }
57257
57258    #[test]
57259    fn test_unpivot_roundtrip_duckdb_cte_columns() {
57260        assert_pivot_roundtrip("WITH unpivot_alias AS (UNPIVOT monthly_sales ON COLUMNS(* EXCLUDE (empid, dept)) INTO NAME month VALUE sales) SELECT * FROM unpivot_alias");
57261    }
57262
57263    #[test]
57264    fn test_unpivot_roundtrip_duckdb_subquery_columns() {
57265        assert_pivot_roundtrip("SELECT * FROM (UNPIVOT monthly_sales ON COLUMNS(* EXCLUDE (empid, dept)) INTO NAME month VALUE sales) AS unpivot_alias");
57266    }
57267
57268    #[test]
57269    fn test_pivot_roundtrip_duckdb_cte_with_columns() {
57270        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)");
57271    }
57272
57273    #[test]
57274    fn test_pivot_roundtrip_standard_first_with_alias() {
57275        // DuckDB fixture #73: comma before FOR is dropped in expected output
57276        let sql = "SELECT * FROM t PIVOT(FIRST(t) AS t, FOR quarter IN ('Q1', 'Q2'))";
57277        let expected = "SELECT * FROM t PIVOT(FIRST(t) AS t FOR quarter IN ('Q1', 'Q2'))";
57278        let parsed = crate::parse(sql, crate::DialectType::DuckDB);
57279        assert!(
57280            parsed.is_ok(),
57281            "Failed to parse: {} - {:?}",
57282            sql,
57283            parsed.err()
57284        );
57285        let stmts = parsed.unwrap();
57286        assert_eq!(stmts.len(), 1);
57287        let generated = crate::generate(&stmts[0], crate::DialectType::DuckDB);
57288        assert!(
57289            generated.is_ok(),
57290            "Failed to generate: {} - {:?}",
57291            sql,
57292            generated.err()
57293        );
57294        let result = generated.unwrap();
57295        assert_eq!(result.trim(), expected, "Round-trip mismatch");
57296    }
57297
57298    #[test]
57299    fn test_pivot_roundtrip_bigquery_implicit_alias() {
57300        // BigQuery fixture #134: implicit aliases become explicit AS
57301        let sql = "SELECT * FROM (SELECT a, b, c FROM test) PIVOT(SUM(b) d, COUNT(*) e FOR c IN ('x', 'y'))";
57302        let expected = "SELECT * FROM (SELECT a, b, c FROM test) PIVOT(SUM(b) AS d, COUNT(*) AS e FOR c IN ('x', 'y'))";
57303        let parsed = crate::parse(sql, crate::DialectType::BigQuery);
57304        assert!(
57305            parsed.is_ok(),
57306            "Failed to parse: {} - {:?}",
57307            sql,
57308            parsed.err()
57309        );
57310        let stmts = parsed.unwrap();
57311        assert_eq!(stmts.len(), 1);
57312        let generated = crate::generate(&stmts[0], crate::DialectType::BigQuery);
57313        assert!(
57314            generated.is_ok(),
57315            "Failed to generate: {} - {:?}",
57316            sql,
57317            generated.err()
57318        );
57319        let result = generated.unwrap();
57320        assert_eq!(result.trim(), expected, "Round-trip mismatch");
57321    }
57322
57323    #[test]
57324    fn test_duckdb_struct_enum_union_row_types() {
57325        use crate::DialectType;
57326
57327        // Helper to test roundtrip with DuckDB dialect - runs in a thread with larger stack
57328        fn check(sql: &str, expected: Option<&str>) {
57329            let sql = sql.to_string();
57330            let expected = expected.map(|s| s.to_string());
57331            let result = std::thread::Builder::new()
57332                .stack_size(16 * 1024 * 1024) // 16MB stack
57333                .spawn(move || {
57334                    let expected_out = expected.as_deref().unwrap_or(&sql);
57335                    let parsed = crate::parse(&sql, DialectType::DuckDB);
57336                    assert!(
57337                        parsed.is_ok(),
57338                        "Failed to parse: {} - {:?}",
57339                        sql,
57340                        parsed.err()
57341                    );
57342                    let stmts = parsed.unwrap();
57343                    assert!(!stmts.is_empty(), "No statements parsed: {}", sql);
57344                    let generated = crate::generate(&stmts[0], DialectType::DuckDB);
57345                    assert!(
57346                        generated.is_ok(),
57347                        "Failed to generate: {} - {:?}",
57348                        sql,
57349                        generated.err()
57350                    );
57351                    let result = generated.unwrap();
57352                    assert_eq!(result.trim(), expected_out, "Mismatch for: {}", sql);
57353                })
57354                .expect("Failed to spawn test thread")
57355                .join();
57356            assert!(result.is_ok(), "Test thread panicked");
57357        }
57358
57359        // UNION type
57360        check("CREATE TABLE tbl1 (u UNION(num INT, str TEXT))", None);
57361        // ENUM type
57362        check(
57363            "CREATE TABLE color (name ENUM('RED', 'GREEN', 'BLUE'))",
57364            None,
57365        );
57366        // ROW type -> STRUCT
57367        check(
57368            "SELECT CAST(ROW(1, 2) AS ROW(a INTEGER, b INTEGER))",
57369            Some("SELECT CAST(ROW(1, 2) AS STRUCT(a INT, b INT))"),
57370        );
57371        // STRUCT with parens
57372        check("CAST(x AS STRUCT(number BIGINT))", None);
57373        // STRUCT with quoted field names
57374        check(
57375            "CAST({'i': 1, 's': 'foo'} AS STRUCT(\"s\" TEXT, \"i\" INT))",
57376            None,
57377        );
57378        // Nested STRUCT
57379        check(
57380            "CAST(ROW(1, ROW(1)) AS STRUCT(number BIGINT, row STRUCT(number BIGINT)))",
57381            None,
57382        );
57383        // STRUCT with array suffix - test just the type parsing part
57384        // Note: STRUCT_PACK -> struct literal transform is a separate feature
57385        check("CAST(x AS STRUCT(a BIGINT)[][])", None);
57386        check("CAST(x AS STRUCT(a BIGINT)[])", None);
57387        // Double-colon cast with STRUCT type
57388        check("CAST({'a': 'b'} AS STRUCT(a TEXT))", None);
57389    }
57390
57391    // Helper for roundtrip identity tests
57392    fn roundtrip(sql: &str) -> String {
57393        let ast =
57394            Parser::parse_sql(sql).unwrap_or_else(|e| panic!("Parse error for '{}': {}", sql, e));
57395        crate::generator::Generator::sql(&ast[0])
57396            .unwrap_or_else(|e| panic!("Generate error for '{}': {}", sql, e))
57397    }
57398
57399    fn assert_roundtrip(sql: &str) {
57400        let result = roundtrip(sql);
57401        assert_eq!(result, sql, "\n  Input:    {}\n  Output:   {}", sql, result);
57402    }
57403
57404    fn assert_roundtrip_expected(sql: &str, expected: &str) {
57405        let result = roundtrip(sql);
57406        assert_eq!(
57407            result, expected,
57408            "\n  Input:    {}\n  Expected: {}\n  Output:   {}",
57409            sql, expected, result
57410        );
57411    }
57412
57413    #[test]
57414    fn test_xmlelement_basic() {
57415        assert_roundtrip("SELECT XMLELEMENT(NAME foo)");
57416    }
57417
57418    #[test]
57419    fn test_xmlelement_with_xmlattributes() {
57420        assert_roundtrip("SELECT XMLELEMENT(NAME foo, XMLATTRIBUTES('xyz' AS bar))");
57421    }
57422
57423    #[test]
57424    fn test_xmlelement_with_multiple_attrs() {
57425        assert_roundtrip("SELECT XMLELEMENT(NAME test, XMLATTRIBUTES(a, b)) FROM test");
57426    }
57427
57428    #[test]
57429    fn test_xmlelement_with_content() {
57430        assert_roundtrip(
57431            "SELECT XMLELEMENT(NAME foo, XMLATTRIBUTES(CURRENT_DATE AS bar), 'cont', 'ent')",
57432        );
57433    }
57434
57435    #[test]
57436    fn test_xmlelement_nested() {
57437        assert_roundtrip("SELECT XMLELEMENT(NAME foo, XMLATTRIBUTES('xyz' AS bar), XMLELEMENT(NAME abc), XMLCOMMENT('test'), XMLELEMENT(NAME xyz))");
57438    }
57439
57440    #[test]
57441    fn test_on_conflict_do_update() {
57442        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");
57443    }
57444
57445    #[test]
57446    fn test_on_conflict_do_nothing() {
57447        // ON CONFLICT(id) is the canonical form (no space before paren)
57448        assert_roundtrip_expected(
57449            "INSERT INTO test (id, name) VALUES (1, 'test') ON CONFLICT (id) DO NOTHING",
57450            "INSERT INTO test (id, name) VALUES (1, 'test') ON CONFLICT(id) DO NOTHING",
57451        );
57452    }
57453
57454    #[test]
57455    fn test_truncate_restart_identity() {
57456        assert_roundtrip("TRUNCATE TABLE t1 RESTART IDENTITY");
57457    }
57458
57459    #[test]
57460    fn test_truncate_restart_identity_restrict() {
57461        assert_roundtrip("TRUNCATE TABLE t1 RESTART IDENTITY RESTRICT");
57462    }
57463
57464    #[test]
57465    fn test_insert_by_name() {
57466        assert_roundtrip("INSERT INTO x BY NAME SELECT 1 AS y");
57467    }
57468
57469    #[test]
57470    fn test_insert_default_values_returning() {
57471        assert_roundtrip("INSERT INTO t DEFAULT VALUES RETURNING (c1)");
57472    }
57473
57474    #[test]
57475    fn test_union_all_by_name() {
57476        assert_roundtrip("SELECT 1 AS x UNION ALL BY NAME SELECT 2 AS x");
57477    }
57478
57479    #[test]
57480    fn test_minus_as_except() {
57481        // MINUS is Oracle/Redshift syntax for EXCEPT
57482        assert_roundtrip_expected(
57483            "SELECT foo, bar FROM table_1 MINUS SELECT foo, bar FROM table_2",
57484            "SELECT foo, bar FROM table_1 EXCEPT SELECT foo, bar FROM table_2",
57485        );
57486    }
57487
57488    #[test]
57489    fn test_filter_without_where() {
57490        assert_roundtrip_expected(
57491            "SELECT SUM(x) FILTER (x = 1)",
57492            "SELECT SUM(x) FILTER(WHERE x = 1)",
57493        );
57494    }
57495
57496    #[test]
57497    fn test_comment_on_materialized_view() {
57498        assert_roundtrip("COMMENT ON MATERIALIZED VIEW my_view IS 'this'");
57499    }
57500
57501    #[test]
57502    fn test_create_index_concurrently() {
57503        assert_roundtrip("CREATE INDEX CONCURRENTLY idx ON t(c)");
57504    }
57505
57506    #[test]
57507    fn test_create_index_if_not_exists() {
57508        assert_roundtrip("CREATE INDEX IF NOT EXISTS idx ON t(c)");
57509    }
57510
57511    #[test]
57512    fn test_alter_table_partition_hive() {
57513        // Hive: ALTER TABLE x PARTITION(y=z) ADD COLUMN a VARCHAR(10)
57514        assert_roundtrip("ALTER TABLE x PARTITION(y = z) ADD COLUMN a VARCHAR(10)");
57515    }
57516
57517    #[test]
57518    fn test_alter_table_change_column_hive() {
57519        // Hive/MySQL: CHANGE COLUMN old_name new_name data_type
57520        assert_roundtrip("ALTER TABLE x CHANGE COLUMN a a VARCHAR(10)");
57521    }
57522
57523    #[test]
57524    fn test_alter_table_add_columns_hive() {
57525        // Hive/Spark: ADD COLUMNS (col1 TYPE, col2 TYPE)
57526        assert_roundtrip("ALTER TABLE X ADD COLUMNS (y INT, z STRING)");
57527    }
57528
57529    #[test]
57530    fn test_alter_table_add_columns_cascade_hive() {
57531        // Hive/Spark: ADD COLUMNS (col1 TYPE, col2 TYPE) CASCADE
57532        assert_roundtrip("ALTER TABLE X ADD COLUMNS (y INT, z STRING) CASCADE");
57533    }
57534
57535    #[test]
57536    fn test_group_by_with_cube() {
57537        // Hive/MySQL: GROUP BY ... WITH CUBE
57538        let sql = "SELECT key, value FROM T1 GROUP BY key, value WITH CUBE";
57539        let result = Parser::parse_sql(sql).unwrap();
57540        let select = result[0].as_select().unwrap();
57541
57542        if let Some(group_by) = &select.group_by {
57543            // Debug: print the expressions
57544            eprintln!("GROUP BY expressions: {:?}", group_by.expressions);
57545
57546            // Check if there's a Cube expression with empty expressions
57547            let has_cube = group_by.expressions.iter().any(|e| {
57548                if let Expression::Cube(c) = e {
57549                    c.expressions.is_empty()
57550                } else {
57551                    false
57552                }
57553            });
57554            assert!(
57555                has_cube,
57556                "Should have a Cube expression with empty expressions in GROUP BY"
57557            );
57558        } else {
57559            panic!("Should have GROUP BY clause");
57560        }
57561    }
57562
57563    #[test]
57564    fn test_group_by_with_rollup() {
57565        // Hive/MySQL: GROUP BY ... WITH ROLLUP
57566        let sql = "SELECT key, value FROM T1 GROUP BY key, value WITH ROLLUP";
57567        let result = Parser::parse_sql(sql).unwrap();
57568        let select = result[0].as_select().unwrap();
57569
57570        if let Some(group_by) = &select.group_by {
57571            // Check if there's a Rollup expression with empty expressions
57572            let has_rollup = group_by.expressions.iter().any(|e| {
57573                if let Expression::Rollup(r) = e {
57574                    r.expressions.is_empty()
57575                } else {
57576                    false
57577                }
57578            });
57579            assert!(
57580                has_rollup,
57581                "Should have a Rollup expression with empty expressions in GROUP BY"
57582            );
57583        } else {
57584            panic!("Should have GROUP BY clause");
57585        }
57586    }
57587}
57588
57589#[cfg(test)]
57590mod join_marker_tests {
57591    use super::*;
57592    use crate::dialects::DialectType;
57593
57594    #[test]
57595    fn test_oracle_join_marker_simple() {
57596        let sql = "select a.baz from a where a.baz = b.baz (+)";
57597        let result = Parser::parse_sql(sql);
57598        println!("Result: {:?}", result);
57599        assert!(result.is_ok(), "Parse error: {:?}", result.err());
57600    }
57601
57602    #[test]
57603    fn test_oracle_join_marker_with_comma_join_and_aliases() {
57604        let sql = "SELECT e1.x, e2.x FROM e e1, e e2 WHERE e1.y = e2.y (+)";
57605        let result = crate::dialects::Dialect::get(DialectType::Oracle).parse(sql);
57606        println!("Result: {:?}", result);
57607        assert!(result.is_ok(), "Parse error: {:?}", result.err());
57608    }
57609
57610    #[test]
57611    fn test_oracle_xmltable_with_quoted_dot_columns() {
57612        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";
57613        let result = crate::dialects::Dialect::get(DialectType::Oracle).parse(sql);
57614        println!("Result: {:?}", result);
57615        assert!(result.is_ok(), "Parse error: {:?}", result.err());
57616    }
57617
57618    #[test]
57619    fn test_oracle_quoted_dot_projection() {
57620        let sql = "SELECT warehouse2.\"Water\", warehouse2.\"Rail\" FROM warehouses warehouse2";
57621        let result = crate::dialects::Dialect::get(DialectType::Oracle).parse(sql);
57622        println!("Result: {:?}", result);
57623        assert!(result.is_ok(), "Parse error: {:?}", result.err());
57624    }
57625
57626    #[test]
57627    fn test_oracle_xmltable_columns_only() {
57628        let sql = "SELECT * FROM XMLTABLE('/Warehouse' PASSING warehouses.warehouse_spec COLUMNS \"Water\" varchar2(6) PATH 'WaterAccess', \"Rail\" varchar2(6) PATH 'RailAccess') warehouse2";
57629        let result = crate::dialects::Dialect::get(DialectType::Oracle).parse(sql);
57630        println!("Result: {:?}", result);
57631        assert!(result.is_ok(), "Parse error: {:?}", result.err());
57632    }
57633
57634    #[test]
57635    fn test_oracle_projection_alias_then_quoted_dot() {
57636        let sql =
57637            "SELECT warehouse_name warehouse, warehouse2.\"Water\" FROM warehouses warehouse2";
57638        let result = crate::dialects::Dialect::get(DialectType::Oracle).parse(sql);
57639        println!("Result: {:?}", result);
57640        assert!(result.is_ok(), "Parse error: {:?}", result.err());
57641    }
57642}
57643
57644#[cfg(test)]
57645mod clickhouse_parser_regression_tests {
57646    use crate::dialects::DialectType;
57647
57648    #[test]
57649    fn test_clickhouse_select_format_clause_not_alias() {
57650        let sql = "SELECT 1 FORMAT TabSeparated";
57651        let result = crate::dialects::Dialect::get(DialectType::ClickHouse).parse(sql);
57652        assert!(result.is_ok(), "Parse error: {:?}", result.err());
57653    }
57654
57655    #[test]
57656    fn test_clickhouse_projection_select_group_by_parses() {
57657        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()";
57658        let result = crate::dialects::Dialect::get(DialectType::ClickHouse).parse(sql);
57659        assert!(result.is_ok(), "Parse error: {:?}", result.err());
57660    }
57661
57662    /// ClickHouse ternary operator AST structure tests.
57663    /// Ported from Python sqlglot: tests/dialects/test_clickhouse.py::test_ternary (lines 765-778).
57664    /// Verifies that `x ? (y ? 1 : 2) : 3` parses into nested IfFunc nodes
57665    /// with the correct AST shape.
57666    #[test]
57667    fn test_clickhouse_ternary_ast_structure() {
57668        use crate::expressions::Expression;
57669
57670        let result = crate::parse_one("x ? (y ? 1 : 2) : 3", DialectType::ClickHouse);
57671        assert!(result.is_ok(), "Parse error: {:?}", result.err());
57672        let ternary = result.unwrap();
57673
57674        // Root should be IfFunc
57675        let if_func = match &ternary {
57676            Expression::IfFunc(f) => f,
57677            other => panic!("Expected IfFunc, got {:?}", std::mem::discriminant(other)),
57678        };
57679
57680        // this (condition) should be Column "x"
57681        assert!(
57682            matches!(&if_func.condition, Expression::Column(_)),
57683            "Expected condition to be Column, got {:?}",
57684            std::mem::discriminant(&if_func.condition)
57685        );
57686
57687        // true branch should be Paren
57688        assert!(
57689            matches!(&if_func.true_value, Expression::Paren(_)),
57690            "Expected true_value to be Paren, got {:?}",
57691            std::mem::discriminant(&if_func.true_value)
57692        );
57693
57694        // false branch should be Literal
57695        let false_value = if_func.false_value.as_ref().expect("Expected false_value");
57696        assert!(
57697            matches!(false_value, Expression::Literal(_)),
57698            "Expected false_value to be Literal, got {:?}",
57699            std::mem::discriminant(false_value)
57700        );
57701
57702        // Inside the Paren, the nested ternary should also be IfFunc
57703        let inner_paren = match &if_func.true_value {
57704            Expression::Paren(p) => p,
57705            _ => unreachable!(),
57706        };
57707        let nested_if = match &inner_paren.this {
57708            Expression::IfFunc(f) => f,
57709            other => panic!(
57710                "Expected nested IfFunc, got {:?}",
57711                std::mem::discriminant(other)
57712            ),
57713        };
57714
57715        // Nested condition should be Column "y"
57716        assert!(
57717            matches!(&nested_if.condition, Expression::Column(_)),
57718            "Expected nested condition to be Column, got {:?}",
57719            std::mem::discriminant(&nested_if.condition)
57720        );
57721
57722        // Nested true should be Literal 1
57723        assert!(
57724            matches!(&nested_if.true_value, Expression::Literal(_)),
57725            "Expected nested true_value to be Literal, got {:?}",
57726            std::mem::discriminant(&nested_if.true_value)
57727        );
57728
57729        // Nested false should be Literal 2
57730        let nested_false = nested_if
57731            .false_value
57732            .as_ref()
57733            .expect("Expected nested false_value");
57734        assert!(
57735            matches!(nested_false, Expression::Literal(_)),
57736            "Expected nested false_value to be Literal, got {:?}",
57737            std::mem::discriminant(nested_false)
57738        );
57739    }
57740
57741    /// Verify that `a AND b ? 1 : 2` has And as the ternary condition
57742    /// (AND binds tighter than ?).
57743    /// Ported from Python sqlglot: test_clickhouse.py line 778.
57744    #[test]
57745    fn test_clickhouse_ternary_and_precedence() {
57746        use crate::expressions::Expression;
57747
57748        let result = crate::parse_one("a and b ? 1 : 2", DialectType::ClickHouse);
57749        assert!(result.is_ok(), "Parse error: {:?}", result.err());
57750        let ternary = result.unwrap();
57751
57752        let if_func = match &ternary {
57753            Expression::IfFunc(f) => f,
57754            other => panic!("Expected IfFunc, got {:?}", std::mem::discriminant(other)),
57755        };
57756
57757        // The condition should be And (not just Column "b")
57758        assert!(
57759            matches!(&if_func.condition, Expression::And(_)),
57760            "Expected condition to be And, got {:?}",
57761            std::mem::discriminant(&if_func.condition)
57762        );
57763    }
57764
57765    #[test]
57766    fn test_parse_interval_bare_number_duckdb() {
57767        use crate::dialects::{Dialect, DialectType};
57768        let sql = "SELECT CAST('2018-01-01 00:00:00' AS DATE) + INTERVAL 3 DAY";
57769        let d = Dialect::get(DialectType::DuckDB);
57770        match d.parse(sql) {
57771            Ok(result) => {
57772                assert!(!result.is_empty(), "Should parse to at least one statement");
57773                // Test transpilation to DuckDB target - should normalize number to quoted string
57774                let output_duckdb = d.transpile_to(sql, DialectType::DuckDB).unwrap();
57775                assert_eq!(
57776                    output_duckdb[0],
57777                    "SELECT CAST('2018-01-01 00:00:00' AS DATE) + INTERVAL '3' DAY",
57778                    "DuckDB output should have quoted interval value"
57779                );
57780                // Test transpilation to Hive target
57781                let output_hive = d.transpile_to(sql, DialectType::Hive).unwrap();
57782                assert_eq!(
57783                    output_hive[0],
57784                    "SELECT CAST('2018-01-01 00:00:00' AS DATE) + INTERVAL '3' DAY",
57785                    "Hive output should have quoted interval value"
57786                );
57787            }
57788            Err(e) => panic!("Failed to parse DuckDB INTERVAL 3 DAY: {}", e),
57789        }
57790    }
57791}