Skip to main content

polyglot_sql/
builder.rs

1//! Fluent SQL Builder API
2//!
3//! Provides a programmatic way to construct SQL [`Expression`] trees without parsing raw SQL
4//! strings. The API mirrors Python sqlglot's builder functions (`select()`, `from_()`,
5//! `condition()`, etc.) and is the primary entry point for constructing queries
6//! programmatically in Rust.
7//!
8//! # Design
9//!
10//! The builder is organized around a few key concepts:
11//!
12//! - **Expression helpers** ([`col`], [`lit`], [`star`], [`null`], [`boolean`], [`func`],
13//!   [`cast`], [`alias`], [`sql_expr`], [`condition`]) create leaf-level [`Expr`] values.
14//! - **Query starters** ([`select`], [`from`], [`delete`], [`insert_into`], [`update`])
15//!   return fluent builder structs ([`SelectBuilder`], [`DeleteBuilder`], etc.).
16//! - **[`Expr`]** wraps an [`Expression`] and exposes operator methods (`.eq()`, `.gt()`,
17//!   `.and()`, `.like()`, etc.) so conditions can be built without manual AST construction.
18//! - **[`IntoExpr`]** and **[`IntoLiteral`]** allow ergonomic coercion of `&str`, `i64`,
19//!   `f64`, and other primitives wherever an expression or literal is expected.
20//!
21//! # Examples
22//!
23//! ```
24//! use polyglot_sql::builder::*;
25//!
26//! // SELECT id, name FROM users WHERE age > 18 ORDER BY name LIMIT 10
27//! let expr = select(["id", "name"])
28//!     .from("users")
29//!     .where_(col("age").gt(lit(18)))
30//!     .order_by(["name"])
31//!     .limit(10)
32//!     .build();
33//! ```
34//!
35//! ```
36//! use polyglot_sql::builder::*;
37//!
38//! // CASE WHEN x > 0 THEN 'positive' ELSE 'non-positive' END
39//! let expr = case()
40//!     .when(col("x").gt(lit(0)), lit("positive"))
41//!     .else_(lit("non-positive"))
42//!     .build();
43//! ```
44//!
45//! ```
46//! use polyglot_sql::builder::*;
47//!
48//! // SELECT id FROM a UNION ALL SELECT id FROM b ORDER BY id LIMIT 5
49//! let expr = union_all(
50//!     select(["id"]).from("a"),
51//!     select(["id"]).from("b"),
52//! )
53//! .order_by(["id"])
54//! .limit(5)
55//! .build();
56//! ```
57
58use crate::expressions::*;
59use crate::generator::Generator;
60use crate::parser::Parser;
61
62fn is_safe_identifier_name(name: &str) -> bool {
63    if name.is_empty() {
64        return false;
65    }
66
67    let mut chars = name.chars();
68    let Some(first) = chars.next() else {
69        return false;
70    };
71
72    if !(first == '_' || first.is_ascii_alphabetic()) {
73        return false;
74    }
75
76    chars.all(|c| c == '_' || c.is_ascii_alphanumeric())
77}
78
79fn builder_identifier(name: &str) -> Identifier {
80    if name == "*" || is_safe_identifier_name(name) {
81        Identifier::new(name)
82    } else {
83        Identifier::quoted(name)
84    }
85}
86
87fn builder_table_ref(name: &str) -> TableRef {
88    let parts: Vec<&str> = name.split('.').collect();
89
90    match parts.len() {
91        3 => {
92            let mut t = TableRef::new(parts[2]);
93            t.name = builder_identifier(parts[2]);
94            t.schema = Some(builder_identifier(parts[1]));
95            t.catalog = Some(builder_identifier(parts[0]));
96            t
97        }
98        2 => {
99            let mut t = TableRef::new(parts[1]);
100            t.name = builder_identifier(parts[1]);
101            t.schema = Some(builder_identifier(parts[0]));
102            t
103        }
104        _ => {
105            let first = parts.first().copied().unwrap_or("");
106            let mut t = TableRef::new(first);
107            t.name = builder_identifier(first);
108            t
109        }
110    }
111}
112
113// ---------------------------------------------------------------------------
114// Expression helpers
115// ---------------------------------------------------------------------------
116
117/// Create a column reference expression.
118///
119/// If `name` contains a dot, it is split on the **last** `.` to produce a table-qualified
120/// column (e.g. `"u.id"` becomes `u.id`). Unqualified names produce a bare column
121/// reference.
122///
123/// # Examples
124///
125/// ```
126/// use polyglot_sql::builder::col;
127///
128/// // Unqualified column
129/// let c = col("name");
130/// assert_eq!(c.to_sql(), "name");
131///
132/// // Table-qualified column
133/// let c = col("users.name");
134/// assert_eq!(c.to_sql(), "users.name");
135/// ```
136pub fn col(name: &str) -> Expr {
137    let parts: Vec<&str> = name.split('.').collect();
138    if parts.len() >= 3 && parts.iter().all(|part| !part.is_empty()) {
139        let mut expr = Expression::boxed_column(Column {
140            name: builder_identifier(parts[1]),
141            table: Some(builder_identifier(parts[0])),
142            join_mark: false,
143            trailing_comments: Vec::new(),
144            span: None,
145            inferred_type: None,
146        });
147
148        for field in &parts[2..] {
149            expr = Expression::Dot(Box::new(DotAccess {
150                this: expr,
151                field: builder_identifier(field),
152            }));
153        }
154
155        return Expr(expr);
156    }
157
158    if let Some((table, column)) = name.rsplit_once('.') {
159        Expr(Expression::boxed_column(Column {
160            name: builder_identifier(column),
161            table: Some(builder_identifier(table)),
162            join_mark: false,
163            trailing_comments: Vec::new(),
164            span: None,
165            inferred_type: None,
166        }))
167    } else {
168        Expr(Expression::boxed_column(Column {
169            name: builder_identifier(name),
170            table: None,
171            join_mark: false,
172            trailing_comments: Vec::new(),
173            span: None,
174            inferred_type: None,
175        }))
176    }
177}
178
179/// Create a literal expression from any type implementing [`IntoLiteral`].
180///
181/// Supported types include `&str` / `String` (string literal), `i32` / `i64` / `usize` /
182/// `f64` (numeric literal), and `bool` (boolean literal).
183///
184/// # Examples
185///
186/// ```
187/// use polyglot_sql::builder::lit;
188///
189/// let s = lit("hello");   // 'hello'
190/// let n = lit(42);        // 42
191/// let f = lit(3.14);      // 3.14
192/// let b = lit(true);      // TRUE
193/// ```
194pub fn lit<V: IntoLiteral>(value: V) -> Expr {
195    value.into_literal()
196}
197
198/// Create a star (`*`) expression, typically used in `SELECT *`.
199pub fn star() -> Expr {
200    Expr(Expression::star())
201}
202
203/// Create a SQL `NULL` literal expression.
204pub fn null() -> Expr {
205    Expr(Expression::Null(Null))
206}
207
208/// Create a SQL boolean literal expression (`TRUE` or `FALSE`).
209pub fn boolean(value: bool) -> Expr {
210    Expr(Expression::Boolean(BooleanLiteral { value }))
211}
212
213/// Create a table reference expression.
214///
215/// The `name` string is split on `.` to determine qualification level:
216///
217/// - `"table"` -- unqualified table reference
218/// - `"schema.table"` -- schema-qualified
219/// - `"catalog.schema.table"` -- fully qualified with catalog
220///
221/// # Examples
222///
223/// ```
224/// use polyglot_sql::builder::table;
225///
226/// let t = table("my_schema.users");
227/// assert_eq!(t.to_sql(), "my_schema.users");
228/// ```
229pub fn table(name: &str) -> Expr {
230    Expr(Expression::Table(Box::new(builder_table_ref(name))))
231}
232
233/// Create a SQL function call expression.
234///
235/// `name` is the function name (e.g. `"COUNT"`, `"UPPER"`, `"COALESCE"`), and `args`
236/// provides zero or more argument expressions.
237///
238/// # Examples
239///
240/// ```
241/// use polyglot_sql::builder::{func, col, star};
242///
243/// let upper = func("UPPER", [col("name")]);
244/// assert_eq!(upper.to_sql(), "UPPER(name)");
245///
246/// let count = func("COUNT", [star()]);
247/// assert_eq!(count.to_sql(), "COUNT(*)");
248/// ```
249pub fn func(name: &str, args: impl IntoIterator<Item = Expr>) -> Expr {
250    Expr(Expression::Function(Box::new(Function {
251        name: name.to_string(),
252        args: args.into_iter().map(|a| a.0).collect(),
253        ..Function::default()
254    })))
255}
256
257/// Create a `CAST(expr AS type)` expression.
258///
259/// The `to` parameter is parsed as a data type name. Common built-in types (`INT`, `BIGINT`,
260/// `VARCHAR`, `BOOLEAN`, `TIMESTAMP`, etc.) are recognized directly. More complex types
261/// (e.g. `"DECIMAL(10,2)"`, `"ARRAY<INT>"`) are parsed via the full SQL parser as a
262/// fallback.
263///
264/// # Examples
265///
266/// ```
267/// use polyglot_sql::builder::{cast, col};
268///
269/// let expr = cast(col("id"), "VARCHAR");
270/// assert_eq!(expr.to_sql(), "CAST(id AS VARCHAR)");
271/// ```
272pub fn cast(expr: Expr, to: &str) -> Expr {
273    let data_type = parse_simple_data_type(to);
274    Expr(Expression::Cast(Box::new(Cast {
275        this: expr.0,
276        to: data_type,
277        trailing_comments: Vec::new(),
278        double_colon_syntax: false,
279        format: None,
280        default: None,
281        inferred_type: None,
282    })))
283}
284
285/// Create a `NOT expr` unary expression.
286///
287/// Wraps the given expression in a logical negation. Equivalent to calling
288/// [`Expr::not()`] on the expression.
289pub fn not(expr: Expr) -> Expr {
290    Expr(Expression::Not(Box::new(UnaryOp::new(expr.0))))
291}
292
293/// Combine two expressions with `AND`.
294///
295/// Equivalent to `left.and(right)`. Useful when you do not have the left-hand side
296/// as the receiver.
297pub fn and(left: Expr, right: Expr) -> Expr {
298    left.and(right)
299}
300
301/// Combine two expressions with `OR`.
302///
303/// Equivalent to `left.or(right)`. Useful when you do not have the left-hand side
304/// as the receiver.
305pub fn or(left: Expr, right: Expr) -> Expr {
306    left.or(right)
307}
308
309/// Create an `expr AS name` alias expression.
310///
311/// This is the free-function form. The method form [`Expr::alias()`] is often more
312/// convenient for chaining.
313pub fn alias(expr: Expr, name: &str) -> Expr {
314    Expr(Expression::Alias(Box::new(Alias {
315        this: expr.0,
316        alias: builder_identifier(name),
317        column_aliases: Vec::new(),
318        alias_explicit_as: false,
319        alias_keyword: None,
320        pre_alias_comments: Vec::new(),
321        trailing_comments: Vec::new(),
322        inferred_type: None,
323    })))
324}
325
326/// Parse a raw SQL expression fragment into an [`Expr`].
327///
328/// Internally wraps the string in `SELECT <sql>`, parses it with the full SQL parser,
329/// and extracts the first expression from the SELECT list. This is useful for
330/// embedding complex SQL fragments (window functions, subquery predicates, etc.)
331/// that would be cumbersome to build purely through the builder API.
332///
333/// # Examples
334///
335/// ```
336/// use polyglot_sql::builder::sql_expr;
337///
338/// let expr = sql_expr("COALESCE(a, b, 0)");
339/// assert_eq!(expr.to_sql(), "COALESCE(a, b, 0)");
340///
341/// let cond = sql_expr("age > 18 AND status = 'active'");
342/// ```
343///
344/// # Panics
345///
346/// Panics if the SQL fragment cannot be parsed, or if the parser fails to extract a
347/// valid expression from the result. Invalid SQL will cause a panic with a message
348/// prefixed by `"sql_expr:"`.
349pub fn sql_expr(sql: &str) -> Expr {
350    let wrapped = format!("SELECT {}", sql);
351    let ast = Parser::parse_sql(&wrapped).expect("sql_expr: failed to parse SQL expression");
352    if let Expression::Select(s) = &ast[0] {
353        if let Some(first) = s.expressions.first() {
354            return Expr(first.clone());
355        }
356    }
357    panic!("sql_expr: failed to extract expression from parsed SQL");
358}
359
360/// Parse a SQL condition string into an [`Expr`].
361///
362/// This is a convenience alias for [`sql_expr()`]. The name `condition` reads more
363/// naturally when the fragment is intended as a WHERE or HAVING predicate.
364///
365/// # Panics
366///
367/// Panics under the same conditions as [`sql_expr()`].
368pub fn condition(sql: &str) -> Expr {
369    sql_expr(sql)
370}
371
372// ---------------------------------------------------------------------------
373// Function helpers — typed AST constructors
374// ---------------------------------------------------------------------------
375
376// -- Aggregates ---------------------------------------------------------------
377
378/// Create a `COUNT(expr)` expression.
379pub fn count(expr: Expr) -> Expr {
380    Expr(Expression::Count(Box::new(CountFunc {
381        this: Some(expr.0),
382        star: false,
383        distinct: false,
384        filter: None,
385        ignore_nulls: None,
386        original_name: None,
387        inferred_type: None,
388    })))
389}
390
391/// Create a `COUNT(*)` expression.
392pub fn count_star() -> Expr {
393    Expr(Expression::Count(Box::new(CountFunc {
394        this: None,
395        star: true,
396        distinct: false,
397        filter: None,
398        ignore_nulls: None,
399        original_name: None,
400        inferred_type: None,
401    })))
402}
403
404/// Create a `COUNT(DISTINCT expr)` expression.
405pub fn count_distinct(expr: Expr) -> Expr {
406    Expr(Expression::Count(Box::new(CountFunc {
407        this: Some(expr.0),
408        star: false,
409        distinct: true,
410        filter: None,
411        ignore_nulls: None,
412        original_name: None,
413        inferred_type: None,
414    })))
415}
416
417/// Create a `SUM(expr)` expression.
418pub fn sum(expr: Expr) -> Expr {
419    Expr(Expression::Sum(Box::new(AggFunc {
420        this: expr.0,
421        distinct: false,
422        filter: None,
423        order_by: vec![],
424        name: None,
425        ignore_nulls: None,
426        having_max: None,
427        limit: None,
428        inferred_type: None,
429    })))
430}
431
432/// Create an `AVG(expr)` expression.
433pub fn avg(expr: Expr) -> Expr {
434    Expr(Expression::Avg(Box::new(AggFunc {
435        this: expr.0,
436        distinct: false,
437        filter: None,
438        order_by: vec![],
439        name: None,
440        ignore_nulls: None,
441        having_max: None,
442        limit: None,
443        inferred_type: None,
444    })))
445}
446
447/// Create a `MIN(expr)` expression. Named `min_` to avoid conflict with `std::cmp::min`.
448pub fn min_(expr: Expr) -> Expr {
449    Expr(Expression::Min(Box::new(AggFunc {
450        this: expr.0,
451        distinct: false,
452        filter: None,
453        order_by: vec![],
454        name: None,
455        ignore_nulls: None,
456        having_max: None,
457        limit: None,
458        inferred_type: None,
459    })))
460}
461
462/// Create a `MAX(expr)` expression. Named `max_` to avoid conflict with `std::cmp::max`.
463pub fn max_(expr: Expr) -> Expr {
464    Expr(Expression::Max(Box::new(AggFunc {
465        this: expr.0,
466        distinct: false,
467        filter: None,
468        order_by: vec![],
469        name: None,
470        ignore_nulls: None,
471        having_max: None,
472        limit: None,
473        inferred_type: None,
474    })))
475}
476
477/// Create an `APPROX_DISTINCT(expr)` expression.
478pub fn approx_distinct(expr: Expr) -> Expr {
479    Expr(Expression::ApproxDistinct(Box::new(AggFunc {
480        this: expr.0,
481        distinct: false,
482        filter: None,
483        order_by: vec![],
484        name: None,
485        ignore_nulls: None,
486        having_max: None,
487        limit: None,
488        inferred_type: None,
489    })))
490}
491
492// -- String functions ---------------------------------------------------------
493
494/// Create an `UPPER(expr)` expression.
495pub fn upper(expr: Expr) -> Expr {
496    Expr(Expression::Upper(Box::new(UnaryFunc::new(expr.0))))
497}
498
499/// Create a `LOWER(expr)` expression.
500pub fn lower(expr: Expr) -> Expr {
501    Expr(Expression::Lower(Box::new(UnaryFunc::new(expr.0))))
502}
503
504/// Create a `LENGTH(expr)` expression.
505pub fn length(expr: Expr) -> Expr {
506    Expr(Expression::Length(Box::new(UnaryFunc::new(expr.0))))
507}
508
509/// Create a `TRIM(expr)` expression.
510pub fn trim(expr: Expr) -> Expr {
511    Expr(Expression::Trim(Box::new(TrimFunc {
512        this: expr.0,
513        characters: None,
514        position: TrimPosition::Both,
515        sql_standard_syntax: false,
516        position_explicit: false,
517    })))
518}
519
520/// Create an `LTRIM(expr)` expression.
521pub fn ltrim(expr: Expr) -> Expr {
522    Expr(Expression::LTrim(Box::new(UnaryFunc::new(expr.0))))
523}
524
525/// Create an `RTRIM(expr)` expression.
526pub fn rtrim(expr: Expr) -> Expr {
527    Expr(Expression::RTrim(Box::new(UnaryFunc::new(expr.0))))
528}
529
530/// Create a `REVERSE(expr)` expression.
531pub fn reverse(expr: Expr) -> Expr {
532    Expr(Expression::Reverse(Box::new(UnaryFunc::new(expr.0))))
533}
534
535/// Create an `INITCAP(expr)` expression.
536pub fn initcap(expr: Expr) -> Expr {
537    Expr(Expression::Initcap(Box::new(UnaryFunc::new(expr.0))))
538}
539
540/// Create a `SUBSTRING(expr, start, len)` expression.
541pub fn substring(expr: Expr, start: Expr, len: Option<Expr>) -> Expr {
542    Expr(Expression::Substring(Box::new(SubstringFunc {
543        this: expr.0,
544        start: start.0,
545        length: len.map(|l| l.0),
546        from_for_syntax: false,
547    })))
548}
549
550/// Create a `REPLACE(expr, old, new)` expression. Named `replace_` to avoid
551/// conflict with the `str::replace` method.
552pub fn replace_(expr: Expr, old: Expr, new: Expr) -> Expr {
553    Expr(Expression::Replace(Box::new(ReplaceFunc {
554        this: expr.0,
555        old: old.0,
556        new: new.0,
557    })))
558}
559
560/// Create a `CONCAT_WS(separator, exprs...)` expression.
561pub fn concat_ws(separator: Expr, exprs: impl IntoIterator<Item = Expr>) -> Expr {
562    Expr(Expression::ConcatWs(Box::new(ConcatWs {
563        separator: separator.0,
564        expressions: exprs.into_iter().map(|e| e.0).collect(),
565    })))
566}
567
568// -- Null handling ------------------------------------------------------------
569
570/// Create a `COALESCE(exprs...)` expression.
571pub fn coalesce(exprs: impl IntoIterator<Item = Expr>) -> Expr {
572    Expr(Expression::Coalesce(Box::new(VarArgFunc {
573        expressions: exprs.into_iter().map(|e| e.0).collect(),
574        original_name: None,
575        inferred_type: None,
576    })))
577}
578
579/// Create a `NULLIF(expr1, expr2)` expression.
580pub fn null_if(expr1: Expr, expr2: Expr) -> Expr {
581    Expr(Expression::NullIf(Box::new(BinaryFunc {
582        this: expr1.0,
583        expression: expr2.0,
584        original_name: None,
585        inferred_type: None,
586    })))
587}
588
589/// Create an `IFNULL(expr, fallback)` expression.
590pub fn if_null(expr: Expr, fallback: Expr) -> Expr {
591    Expr(Expression::IfNull(Box::new(BinaryFunc {
592        this: expr.0,
593        expression: fallback.0,
594        original_name: None,
595        inferred_type: None,
596    })))
597}
598
599// -- Math functions -----------------------------------------------------------
600
601/// Create an `ABS(expr)` expression.
602pub fn abs(expr: Expr) -> Expr {
603    Expr(Expression::Abs(Box::new(UnaryFunc::new(expr.0))))
604}
605
606/// Create a `ROUND(expr, decimals)` expression.
607pub fn round(expr: Expr, decimals: Option<Expr>) -> Expr {
608    Expr(Expression::Round(Box::new(RoundFunc {
609        this: expr.0,
610        decimals: decimals.map(|d| d.0),
611    })))
612}
613
614/// Create a `FLOOR(expr)` expression.
615pub fn floor(expr: Expr) -> Expr {
616    Expr(Expression::Floor(Box::new(FloorFunc {
617        this: expr.0,
618        scale: None,
619        to: None,
620    })))
621}
622
623/// Create a `CEIL(expr)` expression.
624pub fn ceil(expr: Expr) -> Expr {
625    Expr(Expression::Ceil(Box::new(CeilFunc {
626        this: expr.0,
627        decimals: None,
628        to: None,
629    })))
630}
631
632/// Create a `POWER(base, exp)` expression.
633pub fn power(base: Expr, exponent: Expr) -> Expr {
634    Expr(Expression::Power(Box::new(BinaryFunc {
635        this: base.0,
636        expression: exponent.0,
637        original_name: None,
638        inferred_type: None,
639    })))
640}
641
642/// Create a `SQRT(expr)` expression.
643pub fn sqrt(expr: Expr) -> Expr {
644    Expr(Expression::Sqrt(Box::new(UnaryFunc::new(expr.0))))
645}
646
647/// Create a `LN(expr)` expression.
648pub fn ln(expr: Expr) -> Expr {
649    Expr(Expression::Ln(Box::new(UnaryFunc::new(expr.0))))
650}
651
652/// Create an `EXP(expr)` expression. Named `exp_` to avoid conflict with `std::f64::consts`.
653pub fn exp_(expr: Expr) -> Expr {
654    Expr(Expression::Exp(Box::new(UnaryFunc::new(expr.0))))
655}
656
657/// Create a `SIGN(expr)` expression.
658pub fn sign(expr: Expr) -> Expr {
659    Expr(Expression::Sign(Box::new(UnaryFunc::new(expr.0))))
660}
661
662/// Create a `GREATEST(exprs...)` expression.
663pub fn greatest(exprs: impl IntoIterator<Item = Expr>) -> Expr {
664    Expr(Expression::Greatest(Box::new(VarArgFunc {
665        expressions: exprs.into_iter().map(|e| e.0).collect(),
666        original_name: None,
667        inferred_type: None,
668    })))
669}
670
671/// Create a `LEAST(exprs...)` expression.
672pub fn least(exprs: impl IntoIterator<Item = Expr>) -> Expr {
673    Expr(Expression::Least(Box::new(VarArgFunc {
674        expressions: exprs.into_iter().map(|e| e.0).collect(),
675        original_name: None,
676        inferred_type: None,
677    })))
678}
679
680// -- Date/time functions ------------------------------------------------------
681
682/// Create a `CURRENT_DATE` expression.
683pub fn current_date_() -> Expr {
684    Expr(Expression::CurrentDate(CurrentDate))
685}
686
687/// Create a `CURRENT_TIME` expression.
688pub fn current_time_() -> Expr {
689    Expr(Expression::CurrentTime(CurrentTime { precision: None }))
690}
691
692/// Create a `CURRENT_TIMESTAMP` expression.
693pub fn current_timestamp_() -> Expr {
694    Expr(Expression::CurrentTimestamp(CurrentTimestamp {
695        precision: None,
696        sysdate: false,
697    }))
698}
699
700/// Create an `EXTRACT(field FROM expr)` expression.
701pub fn extract_(field: &str, expr: Expr) -> Expr {
702    Expr(Expression::Extract(Box::new(ExtractFunc {
703        this: expr.0,
704        field: parse_datetime_field(field),
705    })))
706}
707
708/// Parse a datetime field name string into a [`DateTimeField`] enum value.
709fn parse_datetime_field(field: &str) -> DateTimeField {
710    match field.to_uppercase().as_str() {
711        "YEAR" => DateTimeField::Year,
712        "MONTH" => DateTimeField::Month,
713        "DAY" => DateTimeField::Day,
714        "HOUR" => DateTimeField::Hour,
715        "MINUTE" => DateTimeField::Minute,
716        "SECOND" => DateTimeField::Second,
717        "MILLISECOND" => DateTimeField::Millisecond,
718        "MICROSECOND" => DateTimeField::Microsecond,
719        "DOW" | "DAYOFWEEK" => DateTimeField::DayOfWeek,
720        "DOY" | "DAYOFYEAR" => DateTimeField::DayOfYear,
721        "WEEK" => DateTimeField::Week,
722        "QUARTER" => DateTimeField::Quarter,
723        "EPOCH" => DateTimeField::Epoch,
724        "TIMEZONE" => DateTimeField::Timezone,
725        "TIMEZONE_HOUR" => DateTimeField::TimezoneHour,
726        "TIMEZONE_MINUTE" => DateTimeField::TimezoneMinute,
727        "DATE" => DateTimeField::Date,
728        "TIME" => DateTimeField::Time,
729        other => DateTimeField::Custom(other.to_string()),
730    }
731}
732
733// -- Window functions ---------------------------------------------------------
734
735/// Create a `ROW_NUMBER()` expression.
736pub fn row_number() -> Expr {
737    Expr(Expression::RowNumber(RowNumber))
738}
739
740/// Create a `RANK()` expression. Named `rank_` to avoid confusion with `Rank` struct.
741pub fn rank_() -> Expr {
742    Expr(Expression::Rank(Rank {
743        order_by: None,
744        args: vec![],
745    }))
746}
747
748/// Create a `DENSE_RANK()` expression.
749pub fn dense_rank() -> Expr {
750    Expr(Expression::DenseRank(DenseRank { args: vec![] }))
751}
752
753// ---------------------------------------------------------------------------
754// Query starters
755// ---------------------------------------------------------------------------
756
757/// Start building a SELECT query with the given column expressions.
758///
759/// Accepts any iterable of items implementing [`IntoExpr`], which includes `&str`
760/// (interpreted as column names), [`Expr`] values, and raw [`Expression`] nodes.
761/// Returns a [`SelectBuilder`] that can be further refined with `.from()`, `.where_()`,
762/// `.order_by()`, etc.
763///
764/// # Examples
765///
766/// ```
767/// use polyglot_sql::builder::*;
768///
769/// // Using string slices (converted to column refs automatically)
770/// let sql = select(["id", "name"]).from("users").to_sql();
771/// assert_eq!(sql, "SELECT id, name FROM users");
772///
773/// // Using Expr values for computed columns
774/// let sql = select([col("price").mul(col("qty")).alias("total")])
775///     .from("items")
776///     .to_sql();
777/// assert_eq!(sql, "SELECT price * qty AS total FROM items");
778/// ```
779pub fn select<I, E>(expressions: I) -> SelectBuilder
780where
781    I: IntoIterator<Item = E>,
782    E: IntoExpr,
783{
784    let mut builder = SelectBuilder::new();
785    for expr in expressions {
786        builder.select = builder.select.column(expr.into_expr().0);
787    }
788    builder
789}
790
791/// Start building a SELECT query beginning with a FROM clause.
792///
793/// Returns a [`SelectBuilder`] with the FROM clause already set. Use
794/// [`SelectBuilder::select_cols()`] to add columns afterward. This is an alternative
795/// entry point for queries where specifying the table first feels more natural.
796///
797/// # Examples
798///
799/// ```
800/// use polyglot_sql::builder::*;
801///
802/// let sql = from("users").select_cols(["id", "name"]).to_sql();
803/// assert_eq!(sql, "SELECT id, name FROM users");
804/// ```
805pub fn from(table_name: &str) -> SelectBuilder {
806    let mut builder = SelectBuilder::new();
807    builder.select.from = Some(From {
808        expressions: vec![Expression::Table(Box::new(builder_table_ref(table_name)))],
809    });
810    builder
811}
812
813/// Start building a `DELETE FROM` statement targeting the given table.
814///
815/// Returns a [`DeleteBuilder`] which supports `.where_()` to add a predicate.
816///
817/// # Examples
818///
819/// ```
820/// use polyglot_sql::builder::*;
821///
822/// let sql = delete("users").where_(col("id").eq(lit(1))).to_sql();
823/// assert_eq!(sql, "DELETE FROM users WHERE id = 1");
824/// ```
825pub fn delete(table_name: &str) -> DeleteBuilder {
826    DeleteBuilder {
827        delete: Delete {
828            table: builder_table_ref(table_name),
829            hint: None,
830            on_cluster: None,
831            alias: None,
832            alias_explicit_as: false,
833            using: Vec::new(),
834            where_clause: None,
835            output: None,
836            leading_comments: Vec::new(),
837            with: None,
838            limit: None,
839            order_by: None,
840            returning: Vec::new(),
841            tables: Vec::new(),
842            tables_from_using: false,
843            joins: Vec::new(),
844            force_index: None,
845            no_from: false,
846        },
847    }
848}
849
850/// Start building an `INSERT INTO` statement targeting the given table.
851///
852/// Returns an [`InsertBuilder`] which supports `.columns()`, `.values()`, and
853/// `.query()` for INSERT ... SELECT.
854///
855/// # Examples
856///
857/// ```
858/// use polyglot_sql::builder::*;
859///
860/// let sql = insert_into("users")
861///     .columns(["id", "name"])
862///     .values([lit(1), lit("Alice")])
863///     .to_sql();
864/// assert_eq!(sql, "INSERT INTO users (id, name) VALUES (1, 'Alice')");
865/// ```
866pub fn insert_into(table_name: &str) -> InsertBuilder {
867    InsertBuilder {
868        insert: Insert {
869            table: builder_table_ref(table_name),
870            columns: Vec::new(),
871            values: Vec::new(),
872            query: None,
873            overwrite: false,
874            partition: Vec::new(),
875            directory: None,
876            returning: Vec::new(),
877            output: None,
878            on_conflict: None,
879            leading_comments: Vec::new(),
880            if_exists: false,
881            with: None,
882            ignore: false,
883            source_alias: None,
884            alias: None,
885            alias_explicit_as: false,
886            default_values: false,
887            by_name: false,
888            conflict_action: None,
889            is_replace: false,
890            hint: None,
891            replace_where: None,
892            source: None,
893            function_target: None,
894            partition_by: None,
895            settings: Vec::new(),
896        },
897    }
898}
899
900/// Start building an `UPDATE` statement targeting the given table.
901///
902/// Returns an [`UpdateBuilder`] which supports `.set()` for column assignments,
903/// `.where_()` for predicates, and `.from()` for PostgreSQL/Snowflake-style
904/// UPDATE ... FROM syntax.
905///
906/// # Examples
907///
908/// ```
909/// use polyglot_sql::builder::*;
910///
911/// let sql = update("users")
912///     .set("name", lit("Bob"))
913///     .where_(col("id").eq(lit(1)))
914///     .to_sql();
915/// assert_eq!(sql, "UPDATE users SET name = 'Bob' WHERE id = 1");
916/// ```
917pub fn update(table_name: &str) -> UpdateBuilder {
918    UpdateBuilder {
919        update: Update {
920            table: builder_table_ref(table_name),
921            hint: None,
922            extra_tables: Vec::new(),
923            table_joins: Vec::new(),
924            set: Vec::new(),
925            from_clause: None,
926            from_joins: Vec::new(),
927            where_clause: None,
928            returning: Vec::new(),
929            output: None,
930            with: None,
931            leading_comments: Vec::new(),
932            limit: None,
933            order_by: None,
934            from_before_set: false,
935        },
936    }
937}
938
939// ---------------------------------------------------------------------------
940// Expr wrapper (for operator methods)
941// ---------------------------------------------------------------------------
942
943/// A thin wrapper around [`Expression`] that provides fluent operator methods.
944///
945/// `Expr` is the primary value type flowing through the builder API. It wraps a single
946/// AST [`Expression`] node and adds convenience methods for comparisons (`.eq()`,
947/// `.gt()`, etc.), logical connectives (`.and()`, `.or()`, `.not()`), arithmetic
948/// (`.add()`, `.sub()`, `.mul()`, `.div()`), pattern matching (`.like()`, `.ilike()`,
949/// `.rlike()`), and other SQL operations (`.in_list()`, `.between()`, `.is_null()`,
950/// `.alias()`, `.cast()`, `.asc()`, `.desc()`).
951///
952/// The inner [`Expression`] is publicly accessible via the `.0` field or
953/// [`Expr::into_inner()`].
954///
955/// # Examples
956///
957/// ```
958/// use polyglot_sql::builder::*;
959///
960/// let condition = col("age").gte(lit(18)).and(col("active").eq(boolean(true)));
961/// assert_eq!(condition.to_sql(), "age >= 18 AND active = TRUE");
962/// ```
963#[derive(Debug, Clone)]
964pub struct Expr(pub Expression);
965
966impl Expr {
967    /// Consume this wrapper and return the inner [`Expression`] node.
968    pub fn into_inner(self) -> Expression {
969        self.0
970    }
971
972    /// Generate a SQL string from this expression using the default (generic) dialect.
973    ///
974    /// Returns an empty string if generation fails.
975    pub fn to_sql(&self) -> String {
976        Generator::sql(&self.0).unwrap_or_default()
977    }
978
979    // -- Comparison operators --
980
981    /// Produce a `self = other` equality comparison.
982    pub fn eq(self, other: Expr) -> Expr {
983        Expr(Expression::Eq(Box::new(binary_op(self.0, other.0))))
984    }
985
986    /// Produce a `self <> other` inequality comparison.
987    pub fn neq(self, other: Expr) -> Expr {
988        Expr(Expression::Neq(Box::new(binary_op(self.0, other.0))))
989    }
990
991    /// Produce a `self < other` less-than comparison.
992    pub fn lt(self, other: Expr) -> Expr {
993        Expr(Expression::Lt(Box::new(binary_op(self.0, other.0))))
994    }
995
996    /// Produce a `self <= other` less-than-or-equal comparison.
997    pub fn lte(self, other: Expr) -> Expr {
998        Expr(Expression::Lte(Box::new(binary_op(self.0, other.0))))
999    }
1000
1001    /// Produce a `self > other` greater-than comparison.
1002    pub fn gt(self, other: Expr) -> Expr {
1003        Expr(Expression::Gt(Box::new(binary_op(self.0, other.0))))
1004    }
1005
1006    /// Produce a `self >= other` greater-than-or-equal comparison.
1007    pub fn gte(self, other: Expr) -> Expr {
1008        Expr(Expression::Gte(Box::new(binary_op(self.0, other.0))))
1009    }
1010
1011    // -- Logical operators --
1012
1013    /// Produce a `self AND other` logical conjunction.
1014    pub fn and(self, other: Expr) -> Expr {
1015        Expr(Expression::And(Box::new(binary_op(self.0, other.0))))
1016    }
1017
1018    /// Produce a `self OR other` logical disjunction.
1019    pub fn or(self, other: Expr) -> Expr {
1020        Expr(Expression::Or(Box::new(binary_op(self.0, other.0))))
1021    }
1022
1023    /// Produce a `NOT self` logical negation.
1024    pub fn not(self) -> Expr {
1025        Expr(Expression::Not(Box::new(UnaryOp::new(self.0))))
1026    }
1027
1028    /// Produce a `self XOR other` logical exclusive-or.
1029    pub fn xor(self, other: Expr) -> Expr {
1030        Expr(Expression::Xor(Box::new(Xor {
1031            this: Some(Box::new(self.0)),
1032            expression: Some(Box::new(other.0)),
1033            expressions: vec![],
1034        })))
1035    }
1036
1037    // -- Arithmetic operators --
1038
1039    /// Produce a `self + other` addition expression.
1040    pub fn add(self, other: Expr) -> Expr {
1041        Expr(Expression::Add(Box::new(binary_op(self.0, other.0))))
1042    }
1043
1044    /// Produce a `self - other` subtraction expression.
1045    pub fn sub(self, other: Expr) -> Expr {
1046        Expr(Expression::Sub(Box::new(binary_op(self.0, other.0))))
1047    }
1048
1049    /// Produce a `self * other` multiplication expression.
1050    pub fn mul(self, other: Expr) -> Expr {
1051        Expr(Expression::Mul(Box::new(binary_op(self.0, other.0))))
1052    }
1053
1054    /// Produce a `self / other` division expression.
1055    pub fn div(self, other: Expr) -> Expr {
1056        Expr(Expression::Div(Box::new(binary_op(self.0, other.0))))
1057    }
1058
1059    // -- Other operators --
1060
1061    /// Produce a `self IS NULL` predicate.
1062    pub fn is_null(self) -> Expr {
1063        Expr(Expression::Is(Box::new(BinaryOp {
1064            left: self.0,
1065            right: Expression::Null(Null),
1066            left_comments: Vec::new(),
1067            operator_comments: Vec::new(),
1068            trailing_comments: Vec::new(),
1069            inferred_type: None,
1070        })))
1071    }
1072
1073    /// Produce a `self IS NOT NULL` predicate (implemented as `NOT (self IS NULL)`).
1074    pub fn is_not_null(self) -> Expr {
1075        Expr(Expression::Not(Box::new(UnaryOp::new(Expression::Is(
1076            Box::new(BinaryOp {
1077                left: self.0,
1078                right: Expression::Null(Null),
1079                left_comments: Vec::new(),
1080                operator_comments: Vec::new(),
1081                trailing_comments: Vec::new(),
1082                inferred_type: None,
1083            }),
1084        )))))
1085    }
1086
1087    /// Produce a `self IN (values...)` membership test.
1088    ///
1089    /// Each element of `values` becomes an item in the parenthesized list.
1090    pub fn in_list(self, values: impl IntoIterator<Item = Expr>) -> Expr {
1091        Expr(Expression::In(Box::new(In {
1092            this: self.0,
1093            expressions: values.into_iter().map(|v| v.0).collect(),
1094            query: None,
1095            not: false,
1096            global: false,
1097            unnest: None,
1098            is_field: false,
1099        })))
1100    }
1101
1102    /// Produce a `self BETWEEN low AND high` range test.
1103    pub fn between(self, low: Expr, high: Expr) -> Expr {
1104        Expr(Expression::Between(Box::new(Between {
1105            this: self.0,
1106            low: low.0,
1107            high: high.0,
1108            not: false,
1109            symmetric: None,
1110        })))
1111    }
1112
1113    /// Produce a `self LIKE pattern` case-sensitive pattern match.
1114    pub fn like(self, pattern: Expr) -> Expr {
1115        Expr(Expression::Like(Box::new(LikeOp {
1116            left: self.0,
1117            right: pattern.0,
1118            escape: None,
1119            quantifier: None,
1120            inferred_type: None,
1121        })))
1122    }
1123
1124    /// Produce a `self AS alias` expression alias.
1125    pub fn alias(self, name: &str) -> Expr {
1126        alias(self, name)
1127    }
1128
1129    /// Produce a `CAST(self AS type)` type conversion.
1130    ///
1131    /// The `to` parameter is parsed as a data type name; see [`cast()`] for details.
1132    pub fn cast(self, to: &str) -> Expr {
1133        cast(self, to)
1134    }
1135
1136    /// Wrap this expression with ascending sort order (`self ASC`).
1137    ///
1138    /// Used in ORDER BY clauses. Expressions without an explicit `.asc()` or `.desc()`
1139    /// call default to ascending order when passed to [`SelectBuilder::order_by()`].
1140    pub fn asc(self) -> Expr {
1141        Expr(Expression::Ordered(Box::new(Ordered {
1142            this: self.0,
1143            desc: false,
1144            nulls_first: None,
1145            explicit_asc: true,
1146            with_fill: None,
1147        })))
1148    }
1149
1150    /// Wrap this expression with descending sort order (`self DESC`).
1151    ///
1152    /// Used in ORDER BY clauses.
1153    pub fn desc(self) -> Expr {
1154        Expr(Expression::Ordered(Box::new(Ordered {
1155            this: self.0,
1156            desc: true,
1157            nulls_first: None,
1158            explicit_asc: false,
1159            with_fill: None,
1160        })))
1161    }
1162
1163    /// Produce a `self ILIKE pattern` case-insensitive pattern match.
1164    ///
1165    /// Supported by PostgreSQL, Snowflake, and other dialects. Dialects that do not
1166    /// support `ILIKE` natively may need transpilation.
1167    pub fn ilike(self, pattern: Expr) -> Expr {
1168        Expr(Expression::ILike(Box::new(LikeOp {
1169            left: self.0,
1170            right: pattern.0,
1171            escape: None,
1172            quantifier: None,
1173            inferred_type: None,
1174        })))
1175    }
1176
1177    /// Produce a `REGEXP_LIKE(self, pattern)` regular expression match.
1178    ///
1179    /// The generated SQL uses the `REGEXP_LIKE` function form. Different dialects may
1180    /// render this as `RLIKE`, `REGEXP`, or `REGEXP_LIKE` after transpilation.
1181    pub fn rlike(self, pattern: Expr) -> Expr {
1182        Expr(Expression::RegexpLike(Box::new(RegexpFunc {
1183            this: self.0,
1184            pattern: pattern.0,
1185            flags: None,
1186        })))
1187    }
1188
1189    /// Produce a `self NOT IN (values...)` negated membership test.
1190    ///
1191    /// Each element of `values` becomes an item in the parenthesized list.
1192    pub fn not_in(self, values: impl IntoIterator<Item = Expr>) -> Expr {
1193        Expr(Expression::In(Box::new(In {
1194            this: self.0,
1195            expressions: values.into_iter().map(|v| v.0).collect(),
1196            query: None,
1197            not: true,
1198            global: false,
1199            unnest: None,
1200            is_field: false,
1201        })))
1202    }
1203}
1204
1205// ---------------------------------------------------------------------------
1206// SelectBuilder
1207// ---------------------------------------------------------------------------
1208
1209/// Fluent builder for constructing `SELECT` statements.
1210///
1211/// Created by the [`select()`] or [`from()`] entry-point functions. Methods on this
1212/// builder return `self` so they can be chained. Call [`.build()`](SelectBuilder::build)
1213/// to obtain an [`Expression`], or [`.to_sql()`](SelectBuilder::to_sql) to generate a
1214/// SQL string directly.
1215///
1216/// # Examples
1217///
1218/// ```
1219/// use polyglot_sql::builder::*;
1220///
1221/// let sql = select(["u.id", "u.name"])
1222///     .from("users")
1223///     .left_join("orders", col("u.id").eq(col("o.user_id")))
1224///     .where_(col("u.active").eq(boolean(true)))
1225///     .group_by(["u.id", "u.name"])
1226///     .order_by([col("u.name").asc()])
1227///     .limit(100)
1228///     .to_sql();
1229/// ```
1230pub struct SelectBuilder {
1231    select: Select,
1232}
1233
1234impl SelectBuilder {
1235    fn new() -> Self {
1236        SelectBuilder {
1237            select: Select::new(),
1238        }
1239    }
1240
1241    /// Append columns to the SELECT list.
1242    ///
1243    /// Accepts any iterable of [`IntoExpr`] items. This is primarily useful when the
1244    /// builder was created via [`from()`] and columns need to be added afterward.
1245    pub fn select_cols<I, E>(mut self, expressions: I) -> Self
1246    where
1247        I: IntoIterator<Item = E>,
1248        E: IntoExpr,
1249    {
1250        for expr in expressions {
1251            self.select.expressions.push(expr.into_expr().0);
1252        }
1253        self
1254    }
1255
1256    /// Set the FROM clause to reference the given table by name.
1257    pub fn from(mut self, table_name: &str) -> Self {
1258        self.select.from = Some(From {
1259            expressions: vec![Expression::Table(Box::new(builder_table_ref(table_name)))],
1260        });
1261        self
1262    }
1263
1264    /// Set the FROM clause to an arbitrary expression (e.g. a subquery or table function).
1265    ///
1266    /// Use this instead of [`SelectBuilder::from()`] when the source is not a simple
1267    /// table name -- for example, a [`subquery()`] or a table-valued function.
1268    pub fn from_expr(mut self, expr: Expr) -> Self {
1269        self.select.from = Some(From {
1270            expressions: vec![expr.0],
1271        });
1272        self
1273    }
1274
1275    /// Add an inner `JOIN` clause with the given ON condition.
1276    pub fn join(mut self, table_name: &str, on: Expr) -> Self {
1277        self.select.joins.push(Join {
1278            kind: JoinKind::Inner,
1279            this: Expression::Table(Box::new(builder_table_ref(table_name))),
1280            on: Some(on.0),
1281            using: Vec::new(),
1282            use_inner_keyword: false,
1283            use_outer_keyword: false,
1284            deferred_condition: false,
1285            join_hint: None,
1286            match_condition: None,
1287            pivots: Vec::new(),
1288            comments: Vec::new(),
1289            nesting_group: 0,
1290            directed: false,
1291        });
1292        self
1293    }
1294
1295    /// Add a `LEFT JOIN` clause with the given ON condition.
1296    pub fn left_join(mut self, table_name: &str, on: Expr) -> Self {
1297        self.select.joins.push(Join {
1298            kind: JoinKind::Left,
1299            this: Expression::Table(Box::new(builder_table_ref(table_name))),
1300            on: Some(on.0),
1301            using: Vec::new(),
1302            use_inner_keyword: false,
1303            use_outer_keyword: false,
1304            deferred_condition: false,
1305            join_hint: None,
1306            match_condition: None,
1307            pivots: Vec::new(),
1308            comments: Vec::new(),
1309            nesting_group: 0,
1310            directed: false,
1311        });
1312        self
1313    }
1314
1315    /// Set the WHERE clause to filter rows by the given condition.
1316    ///
1317    /// Calling this multiple times replaces the previous WHERE condition. To combine
1318    /// multiple predicates, chain them with [`.and()`](Expr::and) or [`.or()`](Expr::or)
1319    /// on a single [`Expr`].
1320    pub fn where_(mut self, condition: Expr) -> Self {
1321        self.select.where_clause = Some(Where { this: condition.0 });
1322        self
1323    }
1324
1325    /// Set the GROUP BY clause with the given grouping expressions.
1326    pub fn group_by<I, E>(mut self, expressions: I) -> Self
1327    where
1328        I: IntoIterator<Item = E>,
1329        E: IntoExpr,
1330    {
1331        self.select.group_by = Some(GroupBy {
1332            expressions: expressions.into_iter().map(|e| e.into_expr().0).collect(),
1333            all: None,
1334            totals: false,
1335            comments: Vec::new(),
1336        });
1337        self
1338    }
1339
1340    /// Set the HAVING clause to filter groups by the given condition.
1341    pub fn having(mut self, condition: Expr) -> Self {
1342        self.select.having = Some(Having {
1343            this: condition.0,
1344            comments: Vec::new(),
1345        });
1346        self
1347    }
1348
1349    /// Set the ORDER BY clause with the given sort expressions.
1350    ///
1351    /// Expressions that are not already wrapped with [`.asc()`](Expr::asc) or
1352    /// [`.desc()`](Expr::desc) default to ascending order. String values are
1353    /// interpreted as column names via [`IntoExpr`].
1354    pub fn order_by<I, E>(mut self, expressions: I) -> Self
1355    where
1356        I: IntoIterator<Item = E>,
1357        E: IntoExpr,
1358    {
1359        self.select.order_by = Some(OrderBy {
1360            siblings: false,
1361            comments: Vec::new(),
1362            expressions: expressions
1363                .into_iter()
1364                .map(|e| {
1365                    let expr = e.into_expr().0;
1366                    match expr {
1367                        Expression::Ordered(_) => expr,
1368                        other => Expression::Ordered(Box::new(Ordered {
1369                            this: other,
1370                            desc: false,
1371                            nulls_first: None,
1372                            explicit_asc: false,
1373                            with_fill: None,
1374                        })),
1375                    }
1376                })
1377                .collect::<Vec<_>>()
1378                .into_iter()
1379                .map(|e| {
1380                    if let Expression::Ordered(o) = e {
1381                        *o
1382                    } else {
1383                        Ordered {
1384                            this: e,
1385                            desc: false,
1386                            nulls_first: None,
1387                            explicit_asc: false,
1388                            with_fill: None,
1389                        }
1390                    }
1391                })
1392                .collect(),
1393        });
1394        self
1395    }
1396
1397    /// Set the SORT BY clause with the given sort expressions.
1398    ///
1399    /// SORT BY is used in Hive/Spark to sort data within each reducer (partition),
1400    /// as opposed to ORDER BY which sorts globally. Expressions that are not already
1401    /// wrapped with [`.asc()`](Expr::asc) or [`.desc()`](Expr::desc) default to
1402    /// ascending order.
1403    pub fn sort_by<I, E>(mut self, expressions: I) -> Self
1404    where
1405        I: IntoIterator<Item = E>,
1406        E: IntoExpr,
1407    {
1408        self.select.sort_by = Some(SortBy {
1409            expressions: expressions
1410                .into_iter()
1411                .map(|e| {
1412                    let expr = e.into_expr().0;
1413                    match expr {
1414                        Expression::Ordered(o) => *o,
1415                        other => Ordered {
1416                            this: other,
1417                            desc: false,
1418                            nulls_first: None,
1419                            explicit_asc: false,
1420                            with_fill: None,
1421                        },
1422                    }
1423                })
1424                .collect(),
1425        });
1426        self
1427    }
1428
1429    /// Set the LIMIT clause to restrict the result set to `count` rows.
1430    pub fn limit(mut self, count: usize) -> Self {
1431        self.select.limit = Some(Limit {
1432            this: Expression::Literal(Box::new(Literal::Number(count.to_string()))),
1433            percent: false,
1434            comments: Vec::new(),
1435        });
1436        self
1437    }
1438
1439    /// Set the OFFSET clause to skip the first `count` rows.
1440    pub fn offset(mut self, count: usize) -> Self {
1441        self.select.offset = Some(Offset {
1442            this: Expression::Literal(Box::new(Literal::Number(count.to_string()))),
1443            rows: None,
1444        });
1445        self
1446    }
1447
1448    /// Enable the DISTINCT modifier on the SELECT clause.
1449    pub fn distinct(mut self) -> Self {
1450        self.select.distinct = true;
1451        self
1452    }
1453
1454    /// Add a QUALIFY clause to filter rows after window function evaluation.
1455    ///
1456    /// QUALIFY is supported by Snowflake, BigQuery, DuckDB, and Databricks. It acts
1457    /// like a WHERE clause but is applied after window functions are computed.
1458    pub fn qualify(mut self, condition: Expr) -> Self {
1459        self.select.qualify = Some(Qualify { this: condition.0 });
1460        self
1461    }
1462
1463    /// Add a `RIGHT JOIN` clause with the given ON condition.
1464    pub fn right_join(mut self, table_name: &str, on: Expr) -> Self {
1465        self.select.joins.push(Join {
1466            kind: JoinKind::Right,
1467            this: Expression::Table(Box::new(builder_table_ref(table_name))),
1468            on: Some(on.0),
1469            using: Vec::new(),
1470            use_inner_keyword: false,
1471            use_outer_keyword: false,
1472            deferred_condition: false,
1473            join_hint: None,
1474            match_condition: None,
1475            pivots: Vec::new(),
1476            comments: Vec::new(),
1477            nesting_group: 0,
1478            directed: false,
1479        });
1480        self
1481    }
1482
1483    /// Add a `CROSS JOIN` clause (Cartesian product, no ON condition).
1484    pub fn cross_join(mut self, table_name: &str) -> Self {
1485        self.select.joins.push(Join {
1486            kind: JoinKind::Cross,
1487            this: Expression::Table(Box::new(builder_table_ref(table_name))),
1488            on: None,
1489            using: Vec::new(),
1490            use_inner_keyword: false,
1491            use_outer_keyword: false,
1492            deferred_condition: false,
1493            join_hint: None,
1494            match_condition: None,
1495            pivots: Vec::new(),
1496            comments: Vec::new(),
1497            nesting_group: 0,
1498            directed: false,
1499        });
1500        self
1501    }
1502
1503    /// Add a `LATERAL VIEW` clause for Hive/Spark user-defined table function (UDTF)
1504    /// expansion.
1505    ///
1506    /// `table_function` is the UDTF expression (e.g. `func("EXPLODE", [col("arr")])`),
1507    /// `table_alias` names the virtual table, and `column_aliases` name the output
1508    /// columns produced by the function.
1509    pub fn lateral_view<S: AsRef<str>>(
1510        mut self,
1511        table_function: Expr,
1512        table_alias: &str,
1513        column_aliases: impl IntoIterator<Item = S>,
1514    ) -> Self {
1515        self.select.lateral_views.push(LateralView {
1516            this: table_function.0,
1517            table_alias: Some(builder_identifier(table_alias)),
1518            column_aliases: column_aliases
1519                .into_iter()
1520                .map(|c| builder_identifier(c.as_ref()))
1521                .collect(),
1522            outer: false,
1523        });
1524        self
1525    }
1526
1527    /// Add a named `WINDOW` clause definition.
1528    ///
1529    /// The window `name` can then be referenced in window function OVER clauses
1530    /// elsewhere in the query. The definition is constructed via [`WindowDefBuilder`].
1531    /// Multiple calls append additional named windows.
1532    pub fn window(mut self, name: &str, def: WindowDefBuilder) -> Self {
1533        let named_window = NamedWindow {
1534            name: builder_identifier(name),
1535            spec: Over {
1536                window_name: None,
1537                partition_by: def.partition_by,
1538                order_by: def.order_by,
1539                frame: None,
1540                alias: None,
1541            },
1542        };
1543        match self.select.windows {
1544            Some(ref mut windows) => windows.push(named_window),
1545            None => self.select.windows = Some(vec![named_window]),
1546        }
1547        self
1548    }
1549
1550    /// Add a `FOR UPDATE` locking clause.
1551    ///
1552    /// Appends a `FOR UPDATE` lock to the SELECT statement. This is used by
1553    /// databases (PostgreSQL, MySQL, Oracle) to lock selected rows for update.
1554    pub fn for_update(mut self) -> Self {
1555        self.select.locks.push(Lock {
1556            update: Some(Box::new(Expression::Boolean(BooleanLiteral {
1557                value: true,
1558            }))),
1559            expressions: vec![],
1560            wait: None,
1561            key: None,
1562        });
1563        self
1564    }
1565
1566    /// Add a `FOR SHARE` locking clause.
1567    ///
1568    /// Appends a `FOR SHARE` lock to the SELECT statement. This allows other
1569    /// transactions to read the locked rows but prevents updates.
1570    pub fn for_share(mut self) -> Self {
1571        self.select.locks.push(Lock {
1572            update: None,
1573            expressions: vec![],
1574            wait: None,
1575            key: None,
1576        });
1577        self
1578    }
1579
1580    /// Add a query hint (e.g., Oracle `/*+ FULL(t) */`).
1581    ///
1582    /// Hints are rendered for Oracle, MySQL, Spark, Hive, Databricks, and PostgreSQL
1583    /// dialects. Multiple calls append additional hints.
1584    pub fn hint(mut self, hint_text: &str) -> Self {
1585        let hint_expr = HintExpression::Raw(hint_text.to_string());
1586        match &mut self.select.hint {
1587            Some(h) => h.expressions.push(hint_expr),
1588            None => {
1589                self.select.hint = Some(Hint {
1590                    expressions: vec![hint_expr],
1591                })
1592            }
1593        }
1594        self
1595    }
1596
1597    /// Convert this SELECT into a `CREATE TABLE AS SELECT` statement.
1598    ///
1599    /// Consumes the builder and returns an [`Expression::CreateTable`] with this
1600    /// query as the `as_select` source.
1601    ///
1602    /// # Examples
1603    ///
1604    /// ```
1605    /// use polyglot_sql::builder::*;
1606    ///
1607    /// let sql = polyglot_sql::generator::Generator::sql(
1608    ///     &select(["*"]).from("t").ctas("new_table")
1609    /// ).unwrap();
1610    /// assert_eq!(sql, "CREATE TABLE new_table AS SELECT * FROM t");
1611    /// ```
1612    pub fn ctas(self, table_name: &str) -> Expression {
1613        Expression::CreateTable(Box::new(CreateTable {
1614            name: builder_table_ref(table_name),
1615            on_cluster: None,
1616            columns: vec![],
1617            constraints: vec![],
1618            if_not_exists: false,
1619            temporary: false,
1620            or_replace: false,
1621            table_modifier: None,
1622            as_select: Some(self.build()),
1623            as_select_parenthesized: false,
1624            on_commit: None,
1625            clone_source: None,
1626            clone_at_clause: None,
1627            is_copy: false,
1628            shallow_clone: false,
1629            deep_clone: false,
1630            leading_comments: vec![],
1631            with_properties: vec![],
1632            teradata_post_name_options: vec![],
1633            with_data: None,
1634            with_statistics: None,
1635            teradata_indexes: vec![],
1636            with_cte: None,
1637            properties: vec![],
1638            partition_of: None,
1639            post_table_properties: vec![],
1640            mysql_table_options: vec![],
1641            inherits: vec![],
1642            on_property: None,
1643            copy_grants: false,
1644            using_template: None,
1645            rollup: None,
1646            uuid: None,
1647            with_partition_columns: vec![],
1648            with_connection: None,
1649        }))
1650    }
1651
1652    /// Combine this SELECT with another via `UNION` (duplicate elimination).
1653    ///
1654    /// Returns a [`SetOpBuilder`] for further chaining (e.g. `.order_by()`, `.limit()`).
1655    pub fn union(self, other: SelectBuilder) -> SetOpBuilder {
1656        SetOpBuilder::new(SetOpKind::Union, self, other, false)
1657    }
1658
1659    /// Combine this SELECT with another via `UNION ALL` (keep duplicates).
1660    ///
1661    /// Returns a [`SetOpBuilder`] for further chaining.
1662    pub fn union_all(self, other: SelectBuilder) -> SetOpBuilder {
1663        SetOpBuilder::new(SetOpKind::Union, self, other, true)
1664    }
1665
1666    /// Combine this SELECT with another via `INTERSECT` (rows common to both).
1667    ///
1668    /// Returns a [`SetOpBuilder`] for further chaining.
1669    pub fn intersect(self, other: SelectBuilder) -> SetOpBuilder {
1670        SetOpBuilder::new(SetOpKind::Intersect, self, other, false)
1671    }
1672
1673    /// Combine this SELECT with another via `EXCEPT` (rows in left but not right).
1674    ///
1675    /// Returns a [`SetOpBuilder`] for further chaining.
1676    pub fn except_(self, other: SelectBuilder) -> SetOpBuilder {
1677        SetOpBuilder::new(SetOpKind::Except, self, other, false)
1678    }
1679
1680    /// Consume this builder and produce the final [`Expression::Select`] AST node.
1681    pub fn build(self) -> Expression {
1682        Expression::Select(Box::new(self.select))
1683    }
1684
1685    /// Consume this builder, generate, and return the SQL string.
1686    ///
1687    /// Equivalent to calling `.build()` followed by [`Generator::sql()`]. Returns an
1688    /// empty string if generation fails.
1689    pub fn to_sql(self) -> String {
1690        Generator::sql(&self.build()).unwrap_or_default()
1691    }
1692}
1693
1694// ---------------------------------------------------------------------------
1695// DeleteBuilder
1696// ---------------------------------------------------------------------------
1697
1698/// Fluent builder for constructing `DELETE FROM` statements.
1699///
1700/// Created by the [`delete()`] entry-point function. Supports an optional `.where_()`
1701/// predicate.
1702pub struct DeleteBuilder {
1703    delete: Delete,
1704}
1705
1706impl DeleteBuilder {
1707    /// Set the WHERE clause to restrict which rows are deleted.
1708    pub fn where_(mut self, condition: Expr) -> Self {
1709        self.delete.where_clause = Some(Where { this: condition.0 });
1710        self
1711    }
1712
1713    /// Consume this builder and produce the final [`Expression::Delete`] AST node.
1714    pub fn build(self) -> Expression {
1715        Expression::Delete(Box::new(self.delete))
1716    }
1717
1718    /// Consume this builder, generate, and return the SQL string.
1719    pub fn to_sql(self) -> String {
1720        Generator::sql(&self.build()).unwrap_or_default()
1721    }
1722}
1723
1724// ---------------------------------------------------------------------------
1725// InsertBuilder
1726// ---------------------------------------------------------------------------
1727
1728/// Fluent builder for constructing `INSERT INTO` statements.
1729///
1730/// Created by the [`insert_into()`] entry-point function. Supports specifying target
1731/// columns via [`.columns()`](InsertBuilder::columns), row values via
1732/// [`.values()`](InsertBuilder::values) (can be called multiple times for multiple rows),
1733/// and INSERT ... SELECT via [`.query()`](InsertBuilder::query).
1734pub struct InsertBuilder {
1735    insert: Insert,
1736}
1737
1738impl InsertBuilder {
1739    /// Set the target column names for the INSERT statement.
1740    pub fn columns<I, S>(mut self, columns: I) -> Self
1741    where
1742        I: IntoIterator<Item = S>,
1743        S: AsRef<str>,
1744    {
1745        self.insert.columns = columns
1746            .into_iter()
1747            .map(|c| builder_identifier(c.as_ref()))
1748            .collect();
1749        self
1750    }
1751
1752    /// Append a row of values to the VALUES clause.
1753    ///
1754    /// Call this method multiple times to insert multiple rows in a single statement.
1755    pub fn values<I>(mut self, values: I) -> Self
1756    where
1757        I: IntoIterator<Item = Expr>,
1758    {
1759        self.insert
1760            .values
1761            .push(values.into_iter().map(|v| v.0).collect());
1762        self
1763    }
1764
1765    /// Set the source query for an `INSERT INTO ... SELECT ...` statement.
1766    ///
1767    /// When a query is set, the VALUES clause is ignored during generation.
1768    pub fn query(mut self, query: SelectBuilder) -> Self {
1769        self.insert.query = Some(query.build());
1770        self
1771    }
1772
1773    /// Consume this builder and produce the final [`Expression::Insert`] AST node.
1774    pub fn build(self) -> Expression {
1775        Expression::Insert(Box::new(self.insert))
1776    }
1777
1778    /// Consume this builder, generate, and return the SQL string.
1779    pub fn to_sql(self) -> String {
1780        Generator::sql(&self.build()).unwrap_or_default()
1781    }
1782}
1783
1784// ---------------------------------------------------------------------------
1785// UpdateBuilder
1786// ---------------------------------------------------------------------------
1787
1788/// Fluent builder for constructing `UPDATE` statements.
1789///
1790/// Created by the [`update()`] entry-point function. Supports column assignments via
1791/// [`.set()`](UpdateBuilder::set), an optional WHERE predicate, and an optional
1792/// FROM clause for PostgreSQL/Snowflake-style multi-table updates.
1793pub struct UpdateBuilder {
1794    update: Update,
1795}
1796
1797impl UpdateBuilder {
1798    /// Add a `SET column = value` assignment.
1799    ///
1800    /// Call this method multiple times to set multiple columns.
1801    pub fn set(mut self, column: &str, value: Expr) -> Self {
1802        self.update.set.push((builder_identifier(column), value.0));
1803        self
1804    }
1805
1806    /// Set the WHERE clause to restrict which rows are updated.
1807    pub fn where_(mut self, condition: Expr) -> Self {
1808        self.update.where_clause = Some(Where { this: condition.0 });
1809        self
1810    }
1811
1812    /// Set the FROM clause for PostgreSQL/Snowflake-style `UPDATE ... FROM ...` syntax.
1813    ///
1814    /// This allows joining against other tables within the UPDATE statement.
1815    pub fn from(mut self, table_name: &str) -> Self {
1816        self.update.from_clause = Some(From {
1817            expressions: vec![Expression::Table(Box::new(builder_table_ref(table_name)))],
1818        });
1819        self
1820    }
1821
1822    /// Consume this builder and produce the final [`Expression::Update`] AST node.
1823    pub fn build(self) -> Expression {
1824        Expression::Update(Box::new(self.update))
1825    }
1826
1827    /// Consume this builder, generate, and return the SQL string.
1828    pub fn to_sql(self) -> String {
1829        Generator::sql(&self.build()).unwrap_or_default()
1830    }
1831}
1832
1833// ---------------------------------------------------------------------------
1834// CaseBuilder
1835// ---------------------------------------------------------------------------
1836
1837/// Start building a searched CASE expression (`CASE WHEN cond THEN result ... END`).
1838///
1839/// A searched CASE evaluates each WHEN condition independently. Use [`case_of()`] for
1840/// a simple CASE that compares an operand against values.
1841///
1842/// # Examples
1843///
1844/// ```
1845/// use polyglot_sql::builder::*;
1846///
1847/// let expr = case()
1848///     .when(col("x").gt(lit(0)), lit("positive"))
1849///     .when(col("x").eq(lit(0)), lit("zero"))
1850///     .else_(lit("negative"))
1851///     .build();
1852/// assert_eq!(
1853///     expr.to_sql(),
1854///     "CASE WHEN x > 0 THEN 'positive' WHEN x = 0 THEN 'zero' ELSE 'negative' END"
1855/// );
1856/// ```
1857pub fn case() -> CaseBuilder {
1858    CaseBuilder {
1859        operand: None,
1860        whens: Vec::new(),
1861        else_: None,
1862    }
1863}
1864
1865/// Start building a simple CASE expression (`CASE operand WHEN value THEN result ... END`).
1866///
1867/// A simple CASE compares the `operand` against each WHEN value for equality. Use
1868/// [`case()`] for a searched CASE with arbitrary boolean conditions.
1869///
1870/// # Examples
1871///
1872/// ```
1873/// use polyglot_sql::builder::*;
1874///
1875/// let expr = case_of(col("status"))
1876///     .when(lit(1), lit("active"))
1877///     .when(lit(0), lit("inactive"))
1878///     .else_(lit("unknown"))
1879///     .build();
1880/// assert_eq!(
1881///     expr.to_sql(),
1882///     "CASE status WHEN 1 THEN 'active' WHEN 0 THEN 'inactive' ELSE 'unknown' END"
1883/// );
1884/// ```
1885pub fn case_of(operand: Expr) -> CaseBuilder {
1886    CaseBuilder {
1887        operand: Some(operand.0),
1888        whens: Vec::new(),
1889        else_: None,
1890    }
1891}
1892
1893/// Fluent builder for SQL `CASE` expressions (both searched and simple forms).
1894///
1895/// Created by [`case()`] (searched form) or [`case_of()`] (simple form). Add branches
1896/// with [`.when()`](CaseBuilder::when) and an optional default with
1897/// [`.else_()`](CaseBuilder::else_). Finalize with [`.build()`](CaseBuilder::build) to
1898/// get an [`Expr`], or [`.build_expr()`](CaseBuilder::build_expr) for a raw
1899/// [`Expression`].
1900pub struct CaseBuilder {
1901    operand: Option<Expression>,
1902    whens: Vec<(Expression, Expression)>,
1903    else_: Option<Expression>,
1904}
1905
1906impl CaseBuilder {
1907    /// Add a `WHEN condition THEN result` branch to the CASE expression.
1908    ///
1909    /// For a searched CASE ([`case()`]), `condition` is a boolean predicate. For a simple
1910    /// CASE ([`case_of()`]), `condition` is the value to compare against the operand.
1911    pub fn when(mut self, condition: Expr, result: Expr) -> Self {
1912        self.whens.push((condition.0, result.0));
1913        self
1914    }
1915
1916    /// Set the `ELSE result` default branch of the CASE expression.
1917    ///
1918    /// If not called, the CASE expression has no ELSE clause (implicitly NULL when
1919    /// no WHEN matches).
1920    pub fn else_(mut self, result: Expr) -> Self {
1921        self.else_ = Some(result.0);
1922        self
1923    }
1924
1925    /// Consume this builder and produce an [`Expr`] wrapping the CASE expression.
1926    pub fn build(self) -> Expr {
1927        Expr(self.build_expr())
1928    }
1929
1930    /// Consume this builder and produce the raw [`Expression::Case`] AST node.
1931    ///
1932    /// Use this instead of [`.build()`](CaseBuilder::build) when you need the
1933    /// [`Expression`] directly rather than an [`Expr`] wrapper.
1934    pub fn build_expr(self) -> Expression {
1935        Expression::Case(Box::new(Case {
1936            operand: self.operand,
1937            whens: self.whens,
1938            else_: self.else_,
1939            comments: Vec::new(),
1940            inferred_type: None,
1941        }))
1942    }
1943}
1944
1945// ---------------------------------------------------------------------------
1946// Subquery builders
1947// ---------------------------------------------------------------------------
1948
1949/// Wrap a [`SelectBuilder`] as a named subquery for use in FROM or JOIN clauses.
1950///
1951/// The resulting [`Expr`] can be passed to [`SelectBuilder::from_expr()`] or used
1952/// in a join condition.
1953///
1954/// # Examples
1955///
1956/// ```
1957/// use polyglot_sql::builder::*;
1958///
1959/// let inner = select(["id", "name"]).from("users").where_(col("active").eq(boolean(true)));
1960/// let sql = select(["sub.id"])
1961///     .from_expr(subquery(inner, "sub"))
1962///     .to_sql();
1963/// assert_eq!(
1964///     sql,
1965///     "SELECT sub.id FROM (SELECT id, name FROM users WHERE active = TRUE) AS sub"
1966/// );
1967/// ```
1968pub fn subquery(query: SelectBuilder, alias_name: &str) -> Expr {
1969    subquery_expr(query.build(), alias_name)
1970}
1971
1972/// Wrap an existing [`Expression`] as a named subquery.
1973///
1974/// This is the lower-level version of [`subquery()`] that accepts a pre-built
1975/// [`Expression`] instead of a [`SelectBuilder`].
1976pub fn subquery_expr(expr: Expression, alias_name: &str) -> Expr {
1977    Expr(Expression::Subquery(Box::new(Subquery {
1978        this: expr,
1979        alias: Some(builder_identifier(alias_name)),
1980        column_aliases: Vec::new(),
1981        alias_explicit_as: false,
1982        alias_keyword: None,
1983        order_by: None,
1984        limit: None,
1985        offset: None,
1986        distribute_by: None,
1987        sort_by: None,
1988        cluster_by: None,
1989        lateral: false,
1990        modifiers_inside: true,
1991        trailing_comments: Vec::new(),
1992        inferred_type: None,
1993    })))
1994}
1995
1996// ---------------------------------------------------------------------------
1997// SetOpBuilder
1998// ---------------------------------------------------------------------------
1999
2000/// Internal enum distinguishing the three kinds of set operations.
2001#[derive(Debug, Clone, Copy)]
2002enum SetOpKind {
2003    Union,
2004    Intersect,
2005    Except,
2006}
2007
2008/// Fluent builder for `UNION`, `INTERSECT`, and `EXCEPT` set operations.
2009///
2010/// Created by the free functions [`union()`], [`union_all()`], [`intersect()`],
2011/// [`intersect_all()`], [`except_()`], [`except_all()`], or the corresponding methods
2012/// on [`SelectBuilder`]. Supports optional `.order_by()`, `.limit()`, and `.offset()`
2013/// clauses applied to the combined result.
2014///
2015/// # Examples
2016///
2017/// ```
2018/// use polyglot_sql::builder::*;
2019///
2020/// let sql = union_all(
2021///     select(["id"]).from("a"),
2022///     select(["id"]).from("b"),
2023/// )
2024/// .order_by(["id"])
2025/// .limit(10)
2026/// .to_sql();
2027/// ```
2028pub struct SetOpBuilder {
2029    kind: SetOpKind,
2030    left: Expression,
2031    right: Expression,
2032    all: bool,
2033    order_by: Option<OrderBy>,
2034    limit: Option<Box<Expression>>,
2035    offset: Option<Box<Expression>>,
2036}
2037
2038impl SetOpBuilder {
2039    fn new(kind: SetOpKind, left: SelectBuilder, right: SelectBuilder, all: bool) -> Self {
2040        SetOpBuilder {
2041            kind,
2042            left: left.build(),
2043            right: right.build(),
2044            all,
2045            order_by: None,
2046            limit: None,
2047            offset: None,
2048        }
2049    }
2050
2051    /// Add an ORDER BY clause applied to the combined set operation result.
2052    ///
2053    /// Expressions not already wrapped with [`.asc()`](Expr::asc) or
2054    /// [`.desc()`](Expr::desc) default to ascending order.
2055    pub fn order_by<I, E>(mut self, expressions: I) -> Self
2056    where
2057        I: IntoIterator<Item = E>,
2058        E: IntoExpr,
2059    {
2060        self.order_by = Some(OrderBy {
2061            siblings: false,
2062            comments: Vec::new(),
2063            expressions: expressions
2064                .into_iter()
2065                .map(|e| {
2066                    let expr = e.into_expr().0;
2067                    match expr {
2068                        Expression::Ordered(o) => *o,
2069                        other => Ordered {
2070                            this: other,
2071                            desc: false,
2072                            nulls_first: None,
2073                            explicit_asc: false,
2074                            with_fill: None,
2075                        },
2076                    }
2077                })
2078                .collect(),
2079        });
2080        self
2081    }
2082
2083    /// Restrict the combined set operation result to `count` rows.
2084    pub fn limit(mut self, count: usize) -> Self {
2085        self.limit = Some(Box::new(Expression::Literal(Box::new(Literal::Number(
2086            count.to_string(),
2087        )))));
2088        self
2089    }
2090
2091    /// Skip the first `count` rows from the combined set operation result.
2092    pub fn offset(mut self, count: usize) -> Self {
2093        self.offset = Some(Box::new(Expression::Literal(Box::new(Literal::Number(
2094            count.to_string(),
2095        )))));
2096        self
2097    }
2098
2099    /// Consume this builder and produce the final set operation [`Expression`] AST node.
2100    ///
2101    /// The returned expression is one of [`Expression::Union`], [`Expression::Intersect`],
2102    /// or [`Expression::Except`] depending on how the builder was created.
2103    pub fn build(self) -> Expression {
2104        match self.kind {
2105            SetOpKind::Union => Expression::Union(Box::new(Union {
2106                left: self.left,
2107                right: self.right,
2108                all: self.all,
2109                distinct: false,
2110                with: None,
2111                order_by: self.order_by,
2112                limit: self.limit,
2113                offset: self.offset,
2114                distribute_by: None,
2115                sort_by: None,
2116                cluster_by: None,
2117                by_name: false,
2118                side: None,
2119                kind: None,
2120                corresponding: false,
2121                strict: false,
2122                on_columns: Vec::new(),
2123            })),
2124            SetOpKind::Intersect => Expression::Intersect(Box::new(Intersect {
2125                left: self.left,
2126                right: self.right,
2127                all: self.all,
2128                distinct: false,
2129                with: None,
2130                order_by: self.order_by,
2131                limit: self.limit,
2132                offset: self.offset,
2133                distribute_by: None,
2134                sort_by: None,
2135                cluster_by: None,
2136                by_name: false,
2137                side: None,
2138                kind: None,
2139                corresponding: false,
2140                strict: false,
2141                on_columns: Vec::new(),
2142            })),
2143            SetOpKind::Except => Expression::Except(Box::new(Except {
2144                left: self.left,
2145                right: self.right,
2146                all: self.all,
2147                distinct: false,
2148                with: None,
2149                order_by: self.order_by,
2150                limit: self.limit,
2151                offset: self.offset,
2152                distribute_by: None,
2153                sort_by: None,
2154                cluster_by: None,
2155                by_name: false,
2156                side: None,
2157                kind: None,
2158                corresponding: false,
2159                strict: false,
2160                on_columns: Vec::new(),
2161            })),
2162        }
2163    }
2164
2165    /// Consume this builder, generate, and return the SQL string.
2166    pub fn to_sql(self) -> String {
2167        Generator::sql(&self.build()).unwrap_or_default()
2168    }
2169}
2170
2171/// Create a `UNION` (duplicate elimination) of two SELECT queries.
2172///
2173/// Returns a [`SetOpBuilder`] for optional ORDER BY / LIMIT / OFFSET chaining.
2174pub fn union(left: SelectBuilder, right: SelectBuilder) -> SetOpBuilder {
2175    SetOpBuilder::new(SetOpKind::Union, left, right, false)
2176}
2177
2178/// Create a `UNION ALL` (keep duplicates) of two SELECT queries.
2179///
2180/// Returns a [`SetOpBuilder`] for optional ORDER BY / LIMIT / OFFSET chaining.
2181pub fn union_all(left: SelectBuilder, right: SelectBuilder) -> SetOpBuilder {
2182    SetOpBuilder::new(SetOpKind::Union, left, right, true)
2183}
2184
2185/// Create an `INTERSECT` (rows common to both) of two SELECT queries.
2186///
2187/// Returns a [`SetOpBuilder`] for optional ORDER BY / LIMIT / OFFSET chaining.
2188pub fn intersect(left: SelectBuilder, right: SelectBuilder) -> SetOpBuilder {
2189    SetOpBuilder::new(SetOpKind::Intersect, left, right, false)
2190}
2191
2192/// Create an `INTERSECT ALL` (keep duplicate common rows) of two SELECT queries.
2193///
2194/// Returns a [`SetOpBuilder`] for optional ORDER BY / LIMIT / OFFSET chaining.
2195pub fn intersect_all(left: SelectBuilder, right: SelectBuilder) -> SetOpBuilder {
2196    SetOpBuilder::new(SetOpKind::Intersect, left, right, true)
2197}
2198
2199/// Create an `EXCEPT` (rows in left but not right) of two SELECT queries.
2200///
2201/// Returns a [`SetOpBuilder`] for optional ORDER BY / LIMIT / OFFSET chaining.
2202pub fn except_(left: SelectBuilder, right: SelectBuilder) -> SetOpBuilder {
2203    SetOpBuilder::new(SetOpKind::Except, left, right, false)
2204}
2205
2206/// Create an `EXCEPT ALL` (keep duplicate difference rows) of two SELECT queries.
2207///
2208/// Returns a [`SetOpBuilder`] for optional ORDER BY / LIMIT / OFFSET chaining.
2209pub fn except_all(left: SelectBuilder, right: SelectBuilder) -> SetOpBuilder {
2210    SetOpBuilder::new(SetOpKind::Except, left, right, true)
2211}
2212
2213// ---------------------------------------------------------------------------
2214// WindowDefBuilder
2215// ---------------------------------------------------------------------------
2216
2217/// Builder for constructing named `WINDOW` clause definitions.
2218///
2219/// Used with [`SelectBuilder::window()`] to define reusable window specifications.
2220/// Supports PARTITION BY and ORDER BY clauses.
2221///
2222/// # Examples
2223///
2224/// ```
2225/// use polyglot_sql::builder::*;
2226///
2227/// let sql = select(["id"])
2228///     .from("t")
2229///     .window(
2230///         "w",
2231///         WindowDefBuilder::new()
2232///             .partition_by(["dept"])
2233///             .order_by([col("salary").desc()]),
2234///     )
2235///     .to_sql();
2236/// ```
2237pub struct WindowDefBuilder {
2238    partition_by: Vec<Expression>,
2239    order_by: Vec<Ordered>,
2240}
2241
2242impl WindowDefBuilder {
2243    /// Create a new, empty window definition builder with no partitioning or ordering.
2244    pub fn new() -> Self {
2245        WindowDefBuilder {
2246            partition_by: Vec::new(),
2247            order_by: Vec::new(),
2248        }
2249    }
2250
2251    /// Set the PARTITION BY expressions for the window definition.
2252    pub fn partition_by<I, E>(mut self, expressions: I) -> Self
2253    where
2254        I: IntoIterator<Item = E>,
2255        E: IntoExpr,
2256    {
2257        self.partition_by = expressions.into_iter().map(|e| e.into_expr().0).collect();
2258        self
2259    }
2260
2261    /// Set the ORDER BY expressions for the window definition.
2262    ///
2263    /// Expressions not already wrapped with [`.asc()`](Expr::asc) or
2264    /// [`.desc()`](Expr::desc) default to ascending order.
2265    pub fn order_by<I, E>(mut self, expressions: I) -> Self
2266    where
2267        I: IntoIterator<Item = E>,
2268        E: IntoExpr,
2269    {
2270        self.order_by = expressions
2271            .into_iter()
2272            .map(|e| {
2273                let expr = e.into_expr().0;
2274                match expr {
2275                    Expression::Ordered(o) => *o,
2276                    other => Ordered {
2277                        this: other,
2278                        desc: false,
2279                        nulls_first: None,
2280                        explicit_asc: false,
2281                        with_fill: None,
2282                    },
2283                }
2284            })
2285            .collect();
2286        self
2287    }
2288}
2289
2290// ---------------------------------------------------------------------------
2291// Trait: IntoExpr
2292// ---------------------------------------------------------------------------
2293
2294/// Conversion trait for types that can be turned into an [`Expr`].
2295///
2296/// This trait is implemented for:
2297///
2298/// - [`Expr`] -- returned as-is.
2299/// - `&str` and `String` -- converted to a column reference via [`col()`].
2300/// - [`Expression`] -- wrapped directly in an [`Expr`].
2301///
2302/// Note: `&str`/`String` inputs are treated as identifiers, not SQL string
2303/// literals. Use [`lit()`] for literal values.
2304///
2305/// It is used as a generic bound throughout the builder API so that functions like
2306/// [`select()`], [`SelectBuilder::order_by()`], and [`SelectBuilder::group_by()`] can
2307/// accept plain strings, [`Expr`] values, or raw [`Expression`] nodes interchangeably.
2308pub trait IntoExpr {
2309    /// Convert this value into an [`Expr`].
2310    fn into_expr(self) -> Expr;
2311}
2312
2313impl IntoExpr for Expr {
2314    fn into_expr(self) -> Expr {
2315        self
2316    }
2317}
2318
2319impl IntoExpr for &str {
2320    /// Convert a string slice to a column reference via [`col()`].
2321    fn into_expr(self) -> Expr {
2322        col(self)
2323    }
2324}
2325
2326impl IntoExpr for String {
2327    /// Convert an owned string to a column reference via [`col()`].
2328    fn into_expr(self) -> Expr {
2329        col(&self)
2330    }
2331}
2332
2333impl IntoExpr for Expression {
2334    /// Wrap a raw [`Expression`] in an [`Expr`].
2335    fn into_expr(self) -> Expr {
2336        Expr(self)
2337    }
2338}
2339
2340// ---------------------------------------------------------------------------
2341// Trait: IntoLiteral
2342// ---------------------------------------------------------------------------
2343
2344/// Conversion trait for types that can be turned into a SQL literal [`Expr`].
2345///
2346/// This trait is used by [`lit()`] to accept various Rust primitive types and convert
2347/// them into the appropriate SQL literal representation.
2348///
2349/// Implemented for:
2350///
2351/// - `&str`, `String` -- produce a SQL string literal (e.g. `'hello'`).
2352/// - `i32`, `i64`, `usize`, `f64` -- produce a SQL numeric literal (e.g. `42`, `3.14`).
2353/// - `bool` -- produce a SQL boolean literal (`TRUE` or `FALSE`).
2354pub trait IntoLiteral {
2355    /// Convert this value into a literal [`Expr`].
2356    fn into_literal(self) -> Expr;
2357}
2358
2359impl IntoLiteral for &str {
2360    /// Produce a SQL string literal (e.g. `'hello'`).
2361    fn into_literal(self) -> Expr {
2362        Expr(Expression::Literal(Box::new(Literal::String(
2363            self.to_string(),
2364        ))))
2365    }
2366}
2367
2368impl IntoLiteral for String {
2369    /// Produce a SQL string literal from an owned string.
2370    fn into_literal(self) -> Expr {
2371        Expr(Expression::Literal(Box::new(Literal::String(self))))
2372    }
2373}
2374
2375impl IntoLiteral for i64 {
2376    /// Produce a SQL numeric literal from a 64-bit integer.
2377    fn into_literal(self) -> Expr {
2378        Expr(Expression::Literal(Box::new(Literal::Number(
2379            self.to_string(),
2380        ))))
2381    }
2382}
2383
2384impl IntoLiteral for i32 {
2385    /// Produce a SQL numeric literal from a 32-bit integer.
2386    fn into_literal(self) -> Expr {
2387        Expr(Expression::Literal(Box::new(Literal::Number(
2388            self.to_string(),
2389        ))))
2390    }
2391}
2392
2393impl IntoLiteral for usize {
2394    /// Produce a SQL numeric literal from a `usize`.
2395    fn into_literal(self) -> Expr {
2396        Expr(Expression::Literal(Box::new(Literal::Number(
2397            self.to_string(),
2398        ))))
2399    }
2400}
2401
2402impl IntoLiteral for f64 {
2403    /// Produce a SQL numeric literal from a 64-bit float.
2404    fn into_literal(self) -> Expr {
2405        Expr(Expression::Literal(Box::new(Literal::Number(
2406            self.to_string(),
2407        ))))
2408    }
2409}
2410
2411impl IntoLiteral for bool {
2412    /// Produce a SQL boolean literal (`TRUE` or `FALSE`).
2413    fn into_literal(self) -> Expr {
2414        Expr(Expression::Boolean(BooleanLiteral { value: self }))
2415    }
2416}
2417
2418// ---------------------------------------------------------------------------
2419// Helpers
2420// ---------------------------------------------------------------------------
2421
2422fn binary_op(left: Expression, right: Expression) -> BinaryOp {
2423    BinaryOp {
2424        left,
2425        right,
2426        left_comments: Vec::new(),
2427        operator_comments: Vec::new(),
2428        trailing_comments: Vec::new(),
2429        inferred_type: None,
2430    }
2431}
2432
2433// ---------------------------------------------------------------------------
2434// MergeBuilder
2435// ---------------------------------------------------------------------------
2436
2437/// Start building a `MERGE INTO` statement targeting the given table.
2438///
2439/// Returns a [`MergeBuilder`] which supports `.using()`, `.when_matched_update()`,
2440/// `.when_matched_delete()`, and `.when_not_matched_insert()`.
2441///
2442/// # Examples
2443///
2444/// ```
2445/// use polyglot_sql::builder::*;
2446///
2447/// let sql = merge_into("target")
2448///     .using("source", col("target.id").eq(col("source.id")))
2449///     .when_matched_update(vec![("name", col("source.name"))])
2450///     .when_not_matched_insert(&["id", "name"], vec![col("source.id"), col("source.name")])
2451///     .to_sql();
2452/// assert!(sql.contains("MERGE INTO"));
2453/// ```
2454pub fn merge_into(target: &str) -> MergeBuilder {
2455    MergeBuilder {
2456        target: Expression::Table(Box::new(builder_table_ref(target))),
2457        using: None,
2458        on: None,
2459        whens: Vec::new(),
2460    }
2461}
2462
2463/// Fluent builder for constructing `MERGE INTO` statements.
2464///
2465/// Created by the [`merge_into()`] entry-point function.
2466pub struct MergeBuilder {
2467    target: Expression,
2468    using: Option<Expression>,
2469    on: Option<Expression>,
2470    whens: Vec<Expression>,
2471}
2472
2473impl MergeBuilder {
2474    /// Set the source table and ON join condition.
2475    pub fn using(mut self, source: &str, on: Expr) -> Self {
2476        self.using = Some(Expression::Table(Box::new(builder_table_ref(source))));
2477        self.on = Some(on.0);
2478        self
2479    }
2480
2481    /// Add a `WHEN MATCHED THEN UPDATE SET` clause.
2482    pub fn when_matched_update(mut self, assignments: Vec<(&str, Expr)>) -> Self {
2483        let eqs: Vec<Expression> = assignments
2484            .into_iter()
2485            .map(|(col_name, val)| {
2486                Expression::Eq(Box::new(BinaryOp {
2487                    left: Expression::boxed_column(Column {
2488                        name: builder_identifier(col_name),
2489                        table: None,
2490                        join_mark: false,
2491                        trailing_comments: Vec::new(),
2492                        span: None,
2493                        inferred_type: None,
2494                    }),
2495                    right: val.0,
2496                    left_comments: Vec::new(),
2497                    operator_comments: Vec::new(),
2498                    trailing_comments: Vec::new(),
2499                    inferred_type: None,
2500                }))
2501            })
2502            .collect();
2503
2504        let action = Expression::Tuple(Box::new(Tuple {
2505            expressions: vec![
2506                Expression::Var(Box::new(Var {
2507                    this: "UPDATE".to_string(),
2508                })),
2509                Expression::Tuple(Box::new(Tuple { expressions: eqs })),
2510            ],
2511        }));
2512
2513        let when = Expression::When(Box::new(When {
2514            matched: Some(Box::new(Expression::Boolean(BooleanLiteral {
2515                value: true,
2516            }))),
2517            source: None,
2518            condition: None,
2519            then: Box::new(action),
2520        }));
2521        self.whens.push(when);
2522        self
2523    }
2524
2525    /// Add a `WHEN MATCHED THEN UPDATE SET` clause with an additional condition.
2526    pub fn when_matched_update_where(
2527        mut self,
2528        condition: Expr,
2529        assignments: Vec<(&str, Expr)>,
2530    ) -> Self {
2531        let eqs: Vec<Expression> = assignments
2532            .into_iter()
2533            .map(|(col_name, val)| {
2534                Expression::Eq(Box::new(BinaryOp {
2535                    left: Expression::boxed_column(Column {
2536                        name: builder_identifier(col_name),
2537                        table: None,
2538                        join_mark: false,
2539                        trailing_comments: Vec::new(),
2540                        span: None,
2541                        inferred_type: None,
2542                    }),
2543                    right: val.0,
2544                    left_comments: Vec::new(),
2545                    operator_comments: Vec::new(),
2546                    trailing_comments: Vec::new(),
2547                    inferred_type: None,
2548                }))
2549            })
2550            .collect();
2551
2552        let action = Expression::Tuple(Box::new(Tuple {
2553            expressions: vec![
2554                Expression::Var(Box::new(Var {
2555                    this: "UPDATE".to_string(),
2556                })),
2557                Expression::Tuple(Box::new(Tuple { expressions: eqs })),
2558            ],
2559        }));
2560
2561        let when = Expression::When(Box::new(When {
2562            matched: Some(Box::new(Expression::Boolean(BooleanLiteral {
2563                value: true,
2564            }))),
2565            source: None,
2566            condition: Some(Box::new(condition.0)),
2567            then: Box::new(action),
2568        }));
2569        self.whens.push(when);
2570        self
2571    }
2572
2573    /// Add a `WHEN MATCHED THEN DELETE` clause.
2574    pub fn when_matched_delete(mut self) -> Self {
2575        let action = Expression::Var(Box::new(Var {
2576            this: "DELETE".to_string(),
2577        }));
2578
2579        let when = Expression::When(Box::new(When {
2580            matched: Some(Box::new(Expression::Boolean(BooleanLiteral {
2581                value: true,
2582            }))),
2583            source: None,
2584            condition: None,
2585            then: Box::new(action),
2586        }));
2587        self.whens.push(when);
2588        self
2589    }
2590
2591    /// Add a `WHEN NOT MATCHED THEN INSERT (cols) VALUES (vals)` clause.
2592    pub fn when_not_matched_insert(mut self, columns: &[&str], values: Vec<Expr>) -> Self {
2593        let col_exprs: Vec<Expression> = columns
2594            .iter()
2595            .map(|c| {
2596                Expression::boxed_column(Column {
2597                    name: builder_identifier(c),
2598                    table: None,
2599                    join_mark: false,
2600                    trailing_comments: Vec::new(),
2601                    span: None,
2602                    inferred_type: None,
2603                })
2604            })
2605            .collect();
2606        let val_exprs: Vec<Expression> = values.into_iter().map(|v| v.0).collect();
2607
2608        let action = Expression::Tuple(Box::new(Tuple {
2609            expressions: vec![
2610                Expression::Var(Box::new(Var {
2611                    this: "INSERT".to_string(),
2612                })),
2613                Expression::Tuple(Box::new(Tuple {
2614                    expressions: col_exprs,
2615                })),
2616                Expression::Tuple(Box::new(Tuple {
2617                    expressions: val_exprs,
2618                })),
2619            ],
2620        }));
2621
2622        let when = Expression::When(Box::new(When {
2623            matched: Some(Box::new(Expression::Boolean(BooleanLiteral {
2624                value: false,
2625            }))),
2626            source: None,
2627            condition: None,
2628            then: Box::new(action),
2629        }));
2630        self.whens.push(when);
2631        self
2632    }
2633
2634    /// Consume this builder and produce the final [`Expression::Merge`] AST node.
2635    pub fn build(self) -> Expression {
2636        let whens_expr = Expression::Whens(Box::new(Whens {
2637            expressions: self.whens,
2638        }));
2639
2640        Expression::Merge(Box::new(Merge {
2641            this: Box::new(self.target),
2642            using: Box::new(
2643                self.using
2644                    .unwrap_or(Expression::Null(crate::expressions::Null)),
2645            ),
2646            on: self.on.map(Box::new),
2647            using_cond: None,
2648            whens: Some(Box::new(whens_expr)),
2649            with_: None,
2650            returning: None,
2651        }))
2652    }
2653
2654    /// Consume this builder, generate, and return the SQL string.
2655    pub fn to_sql(self) -> String {
2656        Generator::sql(&self.build()).unwrap_or_default()
2657    }
2658}
2659
2660fn parse_simple_data_type(name: &str) -> DataType {
2661    let upper = name.trim().to_uppercase();
2662    match upper.as_str() {
2663        "INT" | "INTEGER" => DataType::Int {
2664            length: None,
2665            integer_spelling: upper == "INTEGER",
2666        },
2667        "BIGINT" => DataType::BigInt { length: None },
2668        "SMALLINT" => DataType::SmallInt { length: None },
2669        "TINYINT" => DataType::TinyInt { length: None },
2670        "FLOAT" => DataType::Float {
2671            precision: None,
2672            scale: None,
2673            real_spelling: false,
2674        },
2675        "DOUBLE" => DataType::Double {
2676            precision: None,
2677            scale: None,
2678        },
2679        "BOOLEAN" | "BOOL" => DataType::Boolean,
2680        "TEXT" => DataType::Text,
2681        "DATE" => DataType::Date,
2682        "TIMESTAMP" => DataType::Timestamp {
2683            precision: None,
2684            timezone: false,
2685        },
2686        "VARCHAR" => DataType::VarChar {
2687            length: None,
2688            parenthesized_length: false,
2689        },
2690        "CHAR" => DataType::Char { length: None },
2691        _ => {
2692            // Try to parse as a full type via the parser for complex types
2693            if let Ok(ast) =
2694                crate::parser::Parser::parse_sql(&format!("SELECT CAST(x AS {})", name))
2695            {
2696                if let Expression::Select(s) = &ast[0] {
2697                    if let Some(Expression::Cast(c)) = s.expressions.first() {
2698                        return c.to.clone();
2699                    }
2700                }
2701            }
2702            // Fallback: treat as a custom type
2703            DataType::Custom {
2704                name: name.to_string(),
2705            }
2706        }
2707    }
2708}
2709
2710#[cfg(test)]
2711mod tests {
2712    use super::*;
2713
2714    #[test]
2715    fn test_simple_select() {
2716        let sql = select(["id", "name"]).from("users").to_sql();
2717        assert_eq!(sql, "SELECT id, name FROM users");
2718    }
2719
2720    #[test]
2721    fn test_builder_quotes_unsafe_identifier_tokens() {
2722        let sql = select(["Name; DROP TABLE titanic"]).to_sql();
2723        assert_eq!(sql, r#"SELECT "Name; DROP TABLE titanic""#);
2724    }
2725
2726    #[test]
2727    fn test_builder_string_literal_requires_lit() {
2728        let sql = select([lit("Name; DROP TABLE titanic")]).to_sql();
2729        assert_eq!(sql, "SELECT 'Name; DROP TABLE titanic'");
2730    }
2731
2732    #[test]
2733    fn test_builder_quotes_unsafe_table_name_tokens() {
2734        let sql = select(["id"]).from("users; DROP TABLE x").to_sql();
2735        assert_eq!(sql, r#"SELECT id FROM "users; DROP TABLE x""#);
2736    }
2737
2738    #[test]
2739    fn test_select_star() {
2740        let sql = select([star()]).from("users").to_sql();
2741        assert_eq!(sql, "SELECT * FROM users");
2742    }
2743
2744    #[test]
2745    fn test_select_with_where() {
2746        let sql = select(["id", "name"])
2747            .from("users")
2748            .where_(col("age").gt(lit(18)))
2749            .to_sql();
2750        assert_eq!(sql, "SELECT id, name FROM users WHERE age > 18");
2751    }
2752
2753    #[test]
2754    fn test_select_with_join() {
2755        let sql = select(["u.id", "o.amount"])
2756            .from("users")
2757            .join("orders", col("u.id").eq(col("o.user_id")))
2758            .to_sql();
2759        assert_eq!(
2760            sql,
2761            "SELECT u.id, o.amount FROM users JOIN orders ON u.id = o.user_id"
2762        );
2763    }
2764
2765    #[test]
2766    fn test_select_with_group_by_having() {
2767        let sql = select([col("dept"), func("COUNT", [star()]).alias("cnt")])
2768            .from("employees")
2769            .group_by(["dept"])
2770            .having(func("COUNT", [star()]).gt(lit(5)))
2771            .to_sql();
2772        assert_eq!(
2773            sql,
2774            "SELECT dept, COUNT(*) AS cnt FROM employees GROUP BY dept HAVING COUNT(*) > 5"
2775        );
2776    }
2777
2778    #[test]
2779    fn test_select_with_order_limit_offset() {
2780        let sql = select(["id", "name"])
2781            .from("users")
2782            .order_by(["name"])
2783            .limit(10)
2784            .offset(20)
2785            .to_sql();
2786        assert_eq!(
2787            sql,
2788            "SELECT id, name FROM users ORDER BY name LIMIT 10 OFFSET 20"
2789        );
2790    }
2791
2792    #[test]
2793    fn test_select_distinct() {
2794        let sql = select(["name"]).from("users").distinct().to_sql();
2795        assert_eq!(sql, "SELECT DISTINCT name FROM users");
2796    }
2797
2798    #[test]
2799    fn test_insert_values() {
2800        let sql = insert_into("users")
2801            .columns(["id", "name"])
2802            .values([lit(1), lit("Alice")])
2803            .values([lit(2), lit("Bob")])
2804            .to_sql();
2805        assert_eq!(
2806            sql,
2807            "INSERT INTO users (id, name) VALUES (1, 'Alice'), (2, 'Bob')"
2808        );
2809    }
2810
2811    #[test]
2812    fn test_insert_select() {
2813        let sql = insert_into("archive")
2814            .columns(["id", "name"])
2815            .query(select(["id", "name"]).from("users"))
2816            .to_sql();
2817        assert_eq!(
2818            sql,
2819            "INSERT INTO archive (id, name) SELECT id, name FROM users"
2820        );
2821    }
2822
2823    #[test]
2824    fn test_update() {
2825        let sql = update("users")
2826            .set("name", lit("Bob"))
2827            .set("age", lit(30))
2828            .where_(col("id").eq(lit(1)))
2829            .to_sql();
2830        assert_eq!(sql, "UPDATE users SET name = 'Bob', age = 30 WHERE id = 1");
2831    }
2832
2833    #[test]
2834    fn test_delete() {
2835        let sql = delete("users").where_(col("id").eq(lit(1))).to_sql();
2836        assert_eq!(sql, "DELETE FROM users WHERE id = 1");
2837    }
2838
2839    #[test]
2840    fn test_complex_where() {
2841        let sql = select(["id"])
2842            .from("users")
2843            .where_(
2844                col("age")
2845                    .gte(lit(18))
2846                    .and(col("active").eq(boolean(true)))
2847                    .and(col("name").like(lit("%test%"))),
2848            )
2849            .to_sql();
2850        assert_eq!(
2851            sql,
2852            "SELECT id FROM users WHERE age >= 18 AND active = TRUE AND name LIKE '%test%'"
2853        );
2854    }
2855
2856    #[test]
2857    fn test_in_list() {
2858        let sql = select(["id"])
2859            .from("users")
2860            .where_(col("status").in_list([lit("active"), lit("pending")]))
2861            .to_sql();
2862        assert_eq!(
2863            sql,
2864            "SELECT id FROM users WHERE status IN ('active', 'pending')"
2865        );
2866    }
2867
2868    #[test]
2869    fn test_between() {
2870        let sql = select(["id"])
2871            .from("orders")
2872            .where_(col("amount").between(lit(100), lit(500)))
2873            .to_sql();
2874        assert_eq!(
2875            sql,
2876            "SELECT id FROM orders WHERE amount BETWEEN 100 AND 500"
2877        );
2878    }
2879
2880    #[test]
2881    fn test_is_null() {
2882        let sql = select(["id"])
2883            .from("users")
2884            .where_(col("email").is_null())
2885            .to_sql();
2886        assert_eq!(sql, "SELECT id FROM users WHERE email IS NULL");
2887    }
2888
2889    #[test]
2890    fn test_arithmetic() {
2891        let sql = select([col("price").mul(col("quantity")).alias("total")])
2892            .from("items")
2893            .to_sql();
2894        assert_eq!(sql, "SELECT price * quantity AS total FROM items");
2895    }
2896
2897    #[test]
2898    fn test_cast() {
2899        let sql = select([col("id").cast("VARCHAR")]).from("users").to_sql();
2900        assert_eq!(sql, "SELECT CAST(id AS VARCHAR) FROM users");
2901    }
2902
2903    #[test]
2904    fn test_from_starter() {
2905        let sql = from("users").select_cols(["id", "name"]).to_sql();
2906        assert_eq!(sql, "SELECT id, name FROM users");
2907    }
2908
2909    #[test]
2910    fn test_qualified_column() {
2911        let sql = select([col("u.id"), col("u.name")]).from("users").to_sql();
2912        assert_eq!(sql, "SELECT u.id, u.name FROM users");
2913    }
2914
2915    #[test]
2916    fn test_nested_dot_column() {
2917        let sql = select([col("t.s.f")]).from("users").to_sql();
2918        assert_eq!(sql, "SELECT t.s.f FROM users");
2919    }
2920
2921    #[test]
2922    fn test_not_condition() {
2923        let sql = select(["id"])
2924            .from("users")
2925            .where_(not(col("active").eq(boolean(true))))
2926            .to_sql();
2927        assert_eq!(sql, "SELECT id FROM users WHERE NOT active = TRUE");
2928    }
2929
2930    #[test]
2931    fn test_order_by_desc() {
2932        let sql = select(["id", "name"])
2933            .from("users")
2934            .order_by([col("name").desc()])
2935            .to_sql();
2936        assert_eq!(sql, "SELECT id, name FROM users ORDER BY name DESC");
2937    }
2938
2939    #[test]
2940    fn test_left_join() {
2941        let sql = select(["u.id", "o.amount"])
2942            .from("users")
2943            .left_join("orders", col("u.id").eq(col("o.user_id")))
2944            .to_sql();
2945        assert_eq!(
2946            sql,
2947            "SELECT u.id, o.amount FROM users LEFT JOIN orders ON u.id = o.user_id"
2948        );
2949    }
2950
2951    #[test]
2952    fn test_build_returns_expression() {
2953        let expr = select(["id"]).from("users").build();
2954        assert!(matches!(expr, Expression::Select(_)));
2955    }
2956
2957    #[test]
2958    fn test_expr_interop() {
2959        // Can use Expr in select list
2960        let age_check = col("age").gt(lit(18));
2961        let sql = select([col("id"), age_check.alias("is_adult")])
2962            .from("users")
2963            .to_sql();
2964        assert_eq!(sql, "SELECT id, age > 18 AS is_adult FROM users");
2965    }
2966
2967    // -- Step 2: sql_expr / condition tests --
2968
2969    #[test]
2970    fn test_sql_expr_simple() {
2971        let expr = sql_expr("age > 18");
2972        let sql = select(["id"]).from("users").where_(expr).to_sql();
2973        assert_eq!(sql, "SELECT id FROM users WHERE age > 18");
2974    }
2975
2976    #[test]
2977    fn test_sql_expr_compound() {
2978        let expr = sql_expr("a > 1 AND b < 10");
2979        let sql = select(["*"]).from("t").where_(expr).to_sql();
2980        assert_eq!(sql, "SELECT * FROM t WHERE a > 1 AND b < 10");
2981    }
2982
2983    #[test]
2984    fn test_sql_expr_function() {
2985        let expr = sql_expr("COALESCE(a, b, 0)");
2986        let sql = select([expr.alias("val")]).from("t").to_sql();
2987        assert_eq!(sql, "SELECT COALESCE(a, b, 0) AS val FROM t");
2988    }
2989
2990    #[test]
2991    fn test_condition_alias() {
2992        let cond = condition("x > 0");
2993        let sql = select(["*"]).from("t").where_(cond).to_sql();
2994        assert_eq!(sql, "SELECT * FROM t WHERE x > 0");
2995    }
2996
2997    // -- Step 3: ilike, rlike, not_in tests --
2998
2999    #[test]
3000    fn test_ilike() {
3001        let sql = select(["id"])
3002            .from("users")
3003            .where_(col("name").ilike(lit("%test%")))
3004            .to_sql();
3005        assert_eq!(sql, "SELECT id FROM users WHERE name ILIKE '%test%'");
3006    }
3007
3008    #[test]
3009    fn test_rlike() {
3010        let sql = select(["id"])
3011            .from("users")
3012            .where_(col("name").rlike(lit("^[A-Z]")))
3013            .to_sql();
3014        assert_eq!(
3015            sql,
3016            "SELECT id FROM users WHERE REGEXP_LIKE(name, '^[A-Z]')"
3017        );
3018    }
3019
3020    #[test]
3021    fn test_not_in() {
3022        let sql = select(["id"])
3023            .from("users")
3024            .where_(col("status").not_in([lit("deleted"), lit("banned")]))
3025            .to_sql();
3026        assert_eq!(
3027            sql,
3028            "SELECT id FROM users WHERE NOT status IN ('deleted', 'banned')"
3029        );
3030    }
3031
3032    // -- Step 4: CaseBuilder tests --
3033
3034    #[test]
3035    fn test_case_searched() {
3036        let expr = case()
3037            .when(col("x").gt(lit(0)), lit("positive"))
3038            .when(col("x").eq(lit(0)), lit("zero"))
3039            .else_(lit("negative"))
3040            .build();
3041        let sql = select([expr.alias("label")]).from("t").to_sql();
3042        assert_eq!(
3043            sql,
3044            "SELECT CASE WHEN x > 0 THEN 'positive' WHEN x = 0 THEN 'zero' ELSE 'negative' END AS label FROM t"
3045        );
3046    }
3047
3048    #[test]
3049    fn test_case_simple() {
3050        let expr = case_of(col("status"))
3051            .when(lit(1), lit("active"))
3052            .when(lit(0), lit("inactive"))
3053            .build();
3054        let sql = select([expr.alias("status_label")]).from("t").to_sql();
3055        assert_eq!(
3056            sql,
3057            "SELECT CASE status WHEN 1 THEN 'active' WHEN 0 THEN 'inactive' END AS status_label FROM t"
3058        );
3059    }
3060
3061    #[test]
3062    fn test_case_no_else() {
3063        let expr = case().when(col("x").gt(lit(0)), lit("yes")).build();
3064        let sql = select([expr]).from("t").to_sql();
3065        assert_eq!(sql, "SELECT CASE WHEN x > 0 THEN 'yes' END FROM t");
3066    }
3067
3068    // -- Step 5: subquery tests --
3069
3070    #[test]
3071    fn test_subquery_in_from() {
3072        let inner = select(["id", "name"])
3073            .from("users")
3074            .where_(col("active").eq(boolean(true)));
3075        let outer = select(["sub.id"])
3076            .from_expr(subquery(inner, "sub"))
3077            .to_sql();
3078        assert_eq!(
3079            outer,
3080            "SELECT sub.id FROM (SELECT id, name FROM users WHERE active = TRUE) AS sub"
3081        );
3082    }
3083
3084    #[test]
3085    fn test_subquery_in_join() {
3086        let inner = select([col("user_id"), func("SUM", [col("amount")]).alias("total")])
3087            .from("orders")
3088            .group_by(["user_id"]);
3089        let sql = select(["u.name", "o.total"])
3090            .from("users")
3091            .join("orders", col("u.id").eq(col("o.user_id")))
3092            .to_sql();
3093        assert!(sql.contains("JOIN"));
3094        // Just verify the subquery builder doesn't panic
3095        let _sub = subquery(inner, "o");
3096    }
3097
3098    // -- Step 6: SetOpBuilder tests --
3099
3100    #[test]
3101    fn test_union() {
3102        let sql = union(select(["id"]).from("a"), select(["id"]).from("b")).to_sql();
3103        assert_eq!(sql, "SELECT id FROM a UNION SELECT id FROM b");
3104    }
3105
3106    #[test]
3107    fn test_union_all() {
3108        let sql = union_all(select(["id"]).from("a"), select(["id"]).from("b")).to_sql();
3109        assert_eq!(sql, "SELECT id FROM a UNION ALL SELECT id FROM b");
3110    }
3111
3112    #[test]
3113    fn test_intersect_builder() {
3114        let sql = intersect(select(["id"]).from("a"), select(["id"]).from("b")).to_sql();
3115        assert_eq!(sql, "SELECT id FROM a INTERSECT SELECT id FROM b");
3116    }
3117
3118    #[test]
3119    fn test_except_builder() {
3120        let sql = except_(select(["id"]).from("a"), select(["id"]).from("b")).to_sql();
3121        assert_eq!(sql, "SELECT id FROM a EXCEPT SELECT id FROM b");
3122    }
3123
3124    #[test]
3125    fn test_union_with_order_limit() {
3126        let sql = union(select(["id"]).from("a"), select(["id"]).from("b"))
3127            .order_by(["id"])
3128            .limit(10)
3129            .to_sql();
3130        assert!(sql.contains("UNION"));
3131        assert!(sql.contains("ORDER BY"));
3132        assert!(sql.contains("LIMIT"));
3133    }
3134
3135    #[test]
3136    fn test_select_builder_union() {
3137        let sql = select(["id"])
3138            .from("a")
3139            .union(select(["id"]).from("b"))
3140            .to_sql();
3141        assert_eq!(sql, "SELECT id FROM a UNION SELECT id FROM b");
3142    }
3143
3144    // -- Step 7: SelectBuilder extensions tests --
3145
3146    #[test]
3147    fn test_qualify() {
3148        let sql = select(["id", "name"])
3149            .from("users")
3150            .qualify(col("rn").eq(lit(1)))
3151            .to_sql();
3152        assert_eq!(sql, "SELECT id, name FROM users QUALIFY rn = 1");
3153    }
3154
3155    #[test]
3156    fn test_right_join() {
3157        let sql = select(["u.id", "o.amount"])
3158            .from("users")
3159            .right_join("orders", col("u.id").eq(col("o.user_id")))
3160            .to_sql();
3161        assert_eq!(
3162            sql,
3163            "SELECT u.id, o.amount FROM users RIGHT JOIN orders ON u.id = o.user_id"
3164        );
3165    }
3166
3167    #[test]
3168    fn test_cross_join() {
3169        let sql = select(["a.x", "b.y"]).from("a").cross_join("b").to_sql();
3170        assert_eq!(sql, "SELECT a.x, b.y FROM a CROSS JOIN b");
3171    }
3172
3173    #[test]
3174    fn test_lateral_view() {
3175        let sql = select(["id", "col_val"])
3176            .from("t")
3177            .lateral_view(func("EXPLODE", [col("arr")]), "lv", ["col_val"])
3178            .to_sql();
3179        assert!(sql.contains("LATERAL VIEW"));
3180        assert!(sql.contains("EXPLODE"));
3181    }
3182
3183    #[test]
3184    fn test_window_clause() {
3185        let sql = select(["id"])
3186            .from("t")
3187            .window(
3188                "w",
3189                WindowDefBuilder::new()
3190                    .partition_by(["dept"])
3191                    .order_by(["salary"]),
3192            )
3193            .to_sql();
3194        assert!(sql.contains("WINDOW"));
3195        assert!(sql.contains("PARTITION BY"));
3196    }
3197
3198    // -- XOR operator tests --
3199
3200    #[test]
3201    fn test_xor() {
3202        let sql = select(["*"])
3203            .from("t")
3204            .where_(col("a").xor(col("b")))
3205            .to_sql();
3206        assert_eq!(sql, "SELECT * FROM t WHERE a XOR b");
3207    }
3208
3209    // -- FOR UPDATE / FOR SHARE tests --
3210
3211    #[test]
3212    fn test_for_update() {
3213        let sql = select(["id"]).from("t").for_update().to_sql();
3214        assert_eq!(sql, "SELECT id FROM t FOR UPDATE");
3215    }
3216
3217    #[test]
3218    fn test_for_share() {
3219        let sql = select(["id"]).from("t").for_share().to_sql();
3220        assert_eq!(sql, "SELECT id FROM t FOR SHARE");
3221    }
3222
3223    // -- Hint tests --
3224
3225    #[test]
3226    fn test_hint() {
3227        let sql = select(["*"]).from("t").hint("FULL(t)").to_sql();
3228        assert!(sql.contains("FULL(t)"), "Expected hint in: {}", sql);
3229    }
3230
3231    // -- CTAS tests --
3232
3233    #[test]
3234    fn test_ctas() {
3235        let expr = select(["*"]).from("t").ctas("new_table");
3236        let sql = Generator::sql(&expr).unwrap();
3237        assert_eq!(sql, "CREATE TABLE new_table AS SELECT * FROM t");
3238    }
3239
3240    // -- MergeBuilder tests --
3241
3242    #[test]
3243    fn test_merge_update_insert() {
3244        let sql = merge_into("target")
3245            .using("source", col("target.id").eq(col("source.id")))
3246            .when_matched_update(vec![("name", col("source.name"))])
3247            .when_not_matched_insert(&["id", "name"], vec![col("source.id"), col("source.name")])
3248            .to_sql();
3249        assert!(
3250            sql.contains("MERGE INTO"),
3251            "Expected MERGE INTO in: {}",
3252            sql
3253        );
3254        assert!(sql.contains("USING"), "Expected USING in: {}", sql);
3255        assert!(
3256            sql.contains("WHEN MATCHED"),
3257            "Expected WHEN MATCHED in: {}",
3258            sql
3259        );
3260        assert!(
3261            sql.contains("UPDATE SET"),
3262            "Expected UPDATE SET in: {}",
3263            sql
3264        );
3265        assert!(
3266            sql.contains("WHEN NOT MATCHED"),
3267            "Expected WHEN NOT MATCHED in: {}",
3268            sql
3269        );
3270        assert!(sql.contains("INSERT"), "Expected INSERT in: {}", sql);
3271    }
3272
3273    #[test]
3274    fn test_merge_delete() {
3275        let sql = merge_into("target")
3276            .using("source", col("target.id").eq(col("source.id")))
3277            .when_matched_delete()
3278            .to_sql();
3279        assert!(
3280            sql.contains("MERGE INTO"),
3281            "Expected MERGE INTO in: {}",
3282            sql
3283        );
3284        assert!(
3285            sql.contains("WHEN MATCHED THEN DELETE"),
3286            "Expected WHEN MATCHED THEN DELETE in: {}",
3287            sql
3288        );
3289    }
3290
3291    #[test]
3292    fn test_merge_with_condition() {
3293        let sql = merge_into("target")
3294            .using("source", col("target.id").eq(col("source.id")))
3295            .when_matched_update_where(
3296                col("source.active").eq(boolean(true)),
3297                vec![("name", col("source.name"))],
3298            )
3299            .to_sql();
3300        assert!(
3301            sql.contains("MERGE INTO"),
3302            "Expected MERGE INTO in: {}",
3303            sql
3304        );
3305        assert!(
3306            sql.contains("AND source.active = TRUE"),
3307            "Expected condition in: {}",
3308            sql
3309        );
3310    }
3311}