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            tidb_table_options: vec![],
1642            inherits: vec![],
1643            on_property: None,
1644            copy_grants: false,
1645            using_template: None,
1646            rollup: None,
1647            uuid: None,
1648            with_partition_columns: vec![],
1649            with_connection: None,
1650        }))
1651    }
1652
1653    /// Combine this SELECT with another via `UNION` (duplicate elimination).
1654    ///
1655    /// Returns a [`SetOpBuilder`] for further chaining (e.g. `.order_by()`, `.limit()`).
1656    pub fn union(self, other: SelectBuilder) -> SetOpBuilder {
1657        SetOpBuilder::new(SetOpKind::Union, self, other, false)
1658    }
1659
1660    /// Combine this SELECT with another via `UNION ALL` (keep duplicates).
1661    ///
1662    /// Returns a [`SetOpBuilder`] for further chaining.
1663    pub fn union_all(self, other: SelectBuilder) -> SetOpBuilder {
1664        SetOpBuilder::new(SetOpKind::Union, self, other, true)
1665    }
1666
1667    /// Combine this SELECT with another via `INTERSECT` (rows common to both).
1668    ///
1669    /// Returns a [`SetOpBuilder`] for further chaining.
1670    pub fn intersect(self, other: SelectBuilder) -> SetOpBuilder {
1671        SetOpBuilder::new(SetOpKind::Intersect, self, other, false)
1672    }
1673
1674    /// Combine this SELECT with another via `EXCEPT` (rows in left but not right).
1675    ///
1676    /// Returns a [`SetOpBuilder`] for further chaining.
1677    pub fn except_(self, other: SelectBuilder) -> SetOpBuilder {
1678        SetOpBuilder::new(SetOpKind::Except, self, other, false)
1679    }
1680
1681    /// Consume this builder and produce the final [`Expression::Select`] AST node.
1682    pub fn build(self) -> Expression {
1683        Expression::Select(Box::new(self.select))
1684    }
1685
1686    /// Consume this builder, generate, and return the SQL string.
1687    ///
1688    /// Equivalent to calling `.build()` followed by [`Generator::sql()`]. Returns an
1689    /// empty string if generation fails.
1690    pub fn to_sql(self) -> String {
1691        Generator::sql(&self.build()).unwrap_or_default()
1692    }
1693}
1694
1695// ---------------------------------------------------------------------------
1696// DeleteBuilder
1697// ---------------------------------------------------------------------------
1698
1699/// Fluent builder for constructing `DELETE FROM` statements.
1700///
1701/// Created by the [`delete()`] entry-point function. Supports an optional `.where_()`
1702/// predicate.
1703pub struct DeleteBuilder {
1704    delete: Delete,
1705}
1706
1707impl DeleteBuilder {
1708    /// Set the WHERE clause to restrict which rows are deleted.
1709    pub fn where_(mut self, condition: Expr) -> Self {
1710        self.delete.where_clause = Some(Where { this: condition.0 });
1711        self
1712    }
1713
1714    /// Consume this builder and produce the final [`Expression::Delete`] AST node.
1715    pub fn build(self) -> Expression {
1716        Expression::Delete(Box::new(self.delete))
1717    }
1718
1719    /// Consume this builder, generate, and return the SQL string.
1720    pub fn to_sql(self) -> String {
1721        Generator::sql(&self.build()).unwrap_or_default()
1722    }
1723}
1724
1725// ---------------------------------------------------------------------------
1726// InsertBuilder
1727// ---------------------------------------------------------------------------
1728
1729/// Fluent builder for constructing `INSERT INTO` statements.
1730///
1731/// Created by the [`insert_into()`] entry-point function. Supports specifying target
1732/// columns via [`.columns()`](InsertBuilder::columns), row values via
1733/// [`.values()`](InsertBuilder::values) (can be called multiple times for multiple rows),
1734/// and INSERT ... SELECT via [`.query()`](InsertBuilder::query).
1735pub struct InsertBuilder {
1736    insert: Insert,
1737}
1738
1739impl InsertBuilder {
1740    /// Set the target column names for the INSERT statement.
1741    pub fn columns<I, S>(mut self, columns: I) -> Self
1742    where
1743        I: IntoIterator<Item = S>,
1744        S: AsRef<str>,
1745    {
1746        self.insert.columns = columns
1747            .into_iter()
1748            .map(|c| builder_identifier(c.as_ref()))
1749            .collect();
1750        self
1751    }
1752
1753    /// Append a row of values to the VALUES clause.
1754    ///
1755    /// Call this method multiple times to insert multiple rows in a single statement.
1756    pub fn values<I>(mut self, values: I) -> Self
1757    where
1758        I: IntoIterator<Item = Expr>,
1759    {
1760        self.insert
1761            .values
1762            .push(values.into_iter().map(|v| v.0).collect());
1763        self
1764    }
1765
1766    /// Set the source query for an `INSERT INTO ... SELECT ...` statement.
1767    ///
1768    /// When a query is set, the VALUES clause is ignored during generation.
1769    pub fn query(mut self, query: SelectBuilder) -> Self {
1770        self.insert.query = Some(query.build());
1771        self
1772    }
1773
1774    /// Consume this builder and produce the final [`Expression::Insert`] AST node.
1775    pub fn build(self) -> Expression {
1776        Expression::Insert(Box::new(self.insert))
1777    }
1778
1779    /// Consume this builder, generate, and return the SQL string.
1780    pub fn to_sql(self) -> String {
1781        Generator::sql(&self.build()).unwrap_or_default()
1782    }
1783}
1784
1785// ---------------------------------------------------------------------------
1786// UpdateBuilder
1787// ---------------------------------------------------------------------------
1788
1789/// Fluent builder for constructing `UPDATE` statements.
1790///
1791/// Created by the [`update()`] entry-point function. Supports column assignments via
1792/// [`.set()`](UpdateBuilder::set), an optional WHERE predicate, and an optional
1793/// FROM clause for PostgreSQL/Snowflake-style multi-table updates.
1794pub struct UpdateBuilder {
1795    update: Update,
1796}
1797
1798impl UpdateBuilder {
1799    /// Add a `SET column = value` assignment.
1800    ///
1801    /// Call this method multiple times to set multiple columns.
1802    pub fn set(mut self, column: &str, value: Expr) -> Self {
1803        self.update.set.push((builder_identifier(column), value.0));
1804        self
1805    }
1806
1807    /// Set the WHERE clause to restrict which rows are updated.
1808    pub fn where_(mut self, condition: Expr) -> Self {
1809        self.update.where_clause = Some(Where { this: condition.0 });
1810        self
1811    }
1812
1813    /// Set the FROM clause for PostgreSQL/Snowflake-style `UPDATE ... FROM ...` syntax.
1814    ///
1815    /// This allows joining against other tables within the UPDATE statement.
1816    pub fn from(mut self, table_name: &str) -> Self {
1817        self.update.from_clause = Some(From {
1818            expressions: vec![Expression::Table(Box::new(builder_table_ref(table_name)))],
1819        });
1820        self
1821    }
1822
1823    /// Consume this builder and produce the final [`Expression::Update`] AST node.
1824    pub fn build(self) -> Expression {
1825        Expression::Update(Box::new(self.update))
1826    }
1827
1828    /// Consume this builder, generate, and return the SQL string.
1829    pub fn to_sql(self) -> String {
1830        Generator::sql(&self.build()).unwrap_or_default()
1831    }
1832}
1833
1834// ---------------------------------------------------------------------------
1835// CaseBuilder
1836// ---------------------------------------------------------------------------
1837
1838/// Start building a searched CASE expression (`CASE WHEN cond THEN result ... END`).
1839///
1840/// A searched CASE evaluates each WHEN condition independently. Use [`case_of()`] for
1841/// a simple CASE that compares an operand against values.
1842///
1843/// # Examples
1844///
1845/// ```
1846/// use polyglot_sql::builder::*;
1847///
1848/// let expr = case()
1849///     .when(col("x").gt(lit(0)), lit("positive"))
1850///     .when(col("x").eq(lit(0)), lit("zero"))
1851///     .else_(lit("negative"))
1852///     .build();
1853/// assert_eq!(
1854///     expr.to_sql(),
1855///     "CASE WHEN x > 0 THEN 'positive' WHEN x = 0 THEN 'zero' ELSE 'negative' END"
1856/// );
1857/// ```
1858pub fn case() -> CaseBuilder {
1859    CaseBuilder {
1860        operand: None,
1861        whens: Vec::new(),
1862        else_: None,
1863    }
1864}
1865
1866/// Start building a simple CASE expression (`CASE operand WHEN value THEN result ... END`).
1867///
1868/// A simple CASE compares the `operand` against each WHEN value for equality. Use
1869/// [`case()`] for a searched CASE with arbitrary boolean conditions.
1870///
1871/// # Examples
1872///
1873/// ```
1874/// use polyglot_sql::builder::*;
1875///
1876/// let expr = case_of(col("status"))
1877///     .when(lit(1), lit("active"))
1878///     .when(lit(0), lit("inactive"))
1879///     .else_(lit("unknown"))
1880///     .build();
1881/// assert_eq!(
1882///     expr.to_sql(),
1883///     "CASE status WHEN 1 THEN 'active' WHEN 0 THEN 'inactive' ELSE 'unknown' END"
1884/// );
1885/// ```
1886pub fn case_of(operand: Expr) -> CaseBuilder {
1887    CaseBuilder {
1888        operand: Some(operand.0),
1889        whens: Vec::new(),
1890        else_: None,
1891    }
1892}
1893
1894/// Fluent builder for SQL `CASE` expressions (both searched and simple forms).
1895///
1896/// Created by [`case()`] (searched form) or [`case_of()`] (simple form). Add branches
1897/// with [`.when()`](CaseBuilder::when) and an optional default with
1898/// [`.else_()`](CaseBuilder::else_). Finalize with [`.build()`](CaseBuilder::build) to
1899/// get an [`Expr`], or [`.build_expr()`](CaseBuilder::build_expr) for a raw
1900/// [`Expression`].
1901pub struct CaseBuilder {
1902    operand: Option<Expression>,
1903    whens: Vec<(Expression, Expression)>,
1904    else_: Option<Expression>,
1905}
1906
1907impl CaseBuilder {
1908    /// Add a `WHEN condition THEN result` branch to the CASE expression.
1909    ///
1910    /// For a searched CASE ([`case()`]), `condition` is a boolean predicate. For a simple
1911    /// CASE ([`case_of()`]), `condition` is the value to compare against the operand.
1912    pub fn when(mut self, condition: Expr, result: Expr) -> Self {
1913        self.whens.push((condition.0, result.0));
1914        self
1915    }
1916
1917    /// Set the `ELSE result` default branch of the CASE expression.
1918    ///
1919    /// If not called, the CASE expression has no ELSE clause (implicitly NULL when
1920    /// no WHEN matches).
1921    pub fn else_(mut self, result: Expr) -> Self {
1922        self.else_ = Some(result.0);
1923        self
1924    }
1925
1926    /// Consume this builder and produce an [`Expr`] wrapping the CASE expression.
1927    pub fn build(self) -> Expr {
1928        Expr(self.build_expr())
1929    }
1930
1931    /// Consume this builder and produce the raw [`Expression::Case`] AST node.
1932    ///
1933    /// Use this instead of [`.build()`](CaseBuilder::build) when you need the
1934    /// [`Expression`] directly rather than an [`Expr`] wrapper.
1935    pub fn build_expr(self) -> Expression {
1936        Expression::Case(Box::new(Case {
1937            operand: self.operand,
1938            whens: self.whens,
1939            else_: self.else_,
1940            comments: Vec::new(),
1941            inferred_type: None,
1942        }))
1943    }
1944}
1945
1946// ---------------------------------------------------------------------------
1947// Subquery builders
1948// ---------------------------------------------------------------------------
1949
1950/// Wrap a [`SelectBuilder`] as a named subquery for use in FROM or JOIN clauses.
1951///
1952/// The resulting [`Expr`] can be passed to [`SelectBuilder::from_expr()`] or used
1953/// in a join condition.
1954///
1955/// # Examples
1956///
1957/// ```
1958/// use polyglot_sql::builder::*;
1959///
1960/// let inner = select(["id", "name"]).from("users").where_(col("active").eq(boolean(true)));
1961/// let sql = select(["sub.id"])
1962///     .from_expr(subquery(inner, "sub"))
1963///     .to_sql();
1964/// assert_eq!(
1965///     sql,
1966///     "SELECT sub.id FROM (SELECT id, name FROM users WHERE active = TRUE) AS sub"
1967/// );
1968/// ```
1969pub fn subquery(query: SelectBuilder, alias_name: &str) -> Expr {
1970    subquery_expr(query.build(), alias_name)
1971}
1972
1973/// Wrap an existing [`Expression`] as a named subquery.
1974///
1975/// This is the lower-level version of [`subquery()`] that accepts a pre-built
1976/// [`Expression`] instead of a [`SelectBuilder`].
1977pub fn subquery_expr(expr: Expression, alias_name: &str) -> Expr {
1978    Expr(Expression::Subquery(Box::new(Subquery {
1979        this: expr,
1980        alias: Some(builder_identifier(alias_name)),
1981        column_aliases: Vec::new(),
1982        alias_explicit_as: false,
1983        alias_keyword: None,
1984        order_by: None,
1985        limit: None,
1986        offset: None,
1987        distribute_by: None,
1988        sort_by: None,
1989        cluster_by: None,
1990        lateral: false,
1991        modifiers_inside: true,
1992        trailing_comments: Vec::new(),
1993        inferred_type: None,
1994    })))
1995}
1996
1997// ---------------------------------------------------------------------------
1998// SetOpBuilder
1999// ---------------------------------------------------------------------------
2000
2001/// Internal enum distinguishing the three kinds of set operations.
2002#[derive(Debug, Clone, Copy)]
2003enum SetOpKind {
2004    Union,
2005    Intersect,
2006    Except,
2007}
2008
2009/// Fluent builder for `UNION`, `INTERSECT`, and `EXCEPT` set operations.
2010///
2011/// Created by the free functions [`union()`], [`union_all()`], [`intersect()`],
2012/// [`intersect_all()`], [`except_()`], [`except_all()`], or the corresponding methods
2013/// on [`SelectBuilder`]. Supports optional `.order_by()`, `.limit()`, and `.offset()`
2014/// clauses applied to the combined result.
2015///
2016/// # Examples
2017///
2018/// ```
2019/// use polyglot_sql::builder::*;
2020///
2021/// let sql = union_all(
2022///     select(["id"]).from("a"),
2023///     select(["id"]).from("b"),
2024/// )
2025/// .order_by(["id"])
2026/// .limit(10)
2027/// .to_sql();
2028/// ```
2029pub struct SetOpBuilder {
2030    kind: SetOpKind,
2031    left: Expression,
2032    right: Expression,
2033    all: bool,
2034    order_by: Option<OrderBy>,
2035    limit: Option<Box<Expression>>,
2036    offset: Option<Box<Expression>>,
2037}
2038
2039impl SetOpBuilder {
2040    fn new(kind: SetOpKind, left: SelectBuilder, right: SelectBuilder, all: bool) -> Self {
2041        SetOpBuilder {
2042            kind,
2043            left: left.build(),
2044            right: right.build(),
2045            all,
2046            order_by: None,
2047            limit: None,
2048            offset: None,
2049        }
2050    }
2051
2052    /// Add an ORDER BY clause applied to the combined set operation result.
2053    ///
2054    /// Expressions not already wrapped with [`.asc()`](Expr::asc) or
2055    /// [`.desc()`](Expr::desc) default to ascending order.
2056    pub fn order_by<I, E>(mut self, expressions: I) -> Self
2057    where
2058        I: IntoIterator<Item = E>,
2059        E: IntoExpr,
2060    {
2061        self.order_by = Some(OrderBy {
2062            siblings: false,
2063            comments: Vec::new(),
2064            expressions: expressions
2065                .into_iter()
2066                .map(|e| {
2067                    let expr = e.into_expr().0;
2068                    match expr {
2069                        Expression::Ordered(o) => *o,
2070                        other => Ordered {
2071                            this: other,
2072                            desc: false,
2073                            nulls_first: None,
2074                            explicit_asc: false,
2075                            with_fill: None,
2076                        },
2077                    }
2078                })
2079                .collect(),
2080        });
2081        self
2082    }
2083
2084    /// Restrict the combined set operation result to `count` rows.
2085    pub fn limit(mut self, count: usize) -> Self {
2086        self.limit = Some(Box::new(Expression::Literal(Box::new(Literal::Number(
2087            count.to_string(),
2088        )))));
2089        self
2090    }
2091
2092    /// Skip the first `count` rows from the combined set operation result.
2093    pub fn offset(mut self, count: usize) -> Self {
2094        self.offset = Some(Box::new(Expression::Literal(Box::new(Literal::Number(
2095            count.to_string(),
2096        )))));
2097        self
2098    }
2099
2100    /// Consume this builder and produce the final set operation [`Expression`] AST node.
2101    ///
2102    /// The returned expression is one of [`Expression::Union`], [`Expression::Intersect`],
2103    /// or [`Expression::Except`] depending on how the builder was created.
2104    pub fn build(self) -> Expression {
2105        match self.kind {
2106            SetOpKind::Union => Expression::Union(Box::new(Union {
2107                left: self.left,
2108                right: self.right,
2109                all: self.all,
2110                distinct: false,
2111                with: None,
2112                order_by: self.order_by,
2113                limit: self.limit,
2114                offset: self.offset,
2115                distribute_by: None,
2116                sort_by: None,
2117                cluster_by: None,
2118                by_name: false,
2119                side: None,
2120                kind: None,
2121                corresponding: false,
2122                strict: false,
2123                on_columns: Vec::new(),
2124            })),
2125            SetOpKind::Intersect => Expression::Intersect(Box::new(Intersect {
2126                left: self.left,
2127                right: self.right,
2128                all: self.all,
2129                distinct: false,
2130                with: None,
2131                order_by: self.order_by,
2132                limit: self.limit,
2133                offset: self.offset,
2134                distribute_by: None,
2135                sort_by: None,
2136                cluster_by: None,
2137                by_name: false,
2138                side: None,
2139                kind: None,
2140                corresponding: false,
2141                strict: false,
2142                on_columns: Vec::new(),
2143            })),
2144            SetOpKind::Except => Expression::Except(Box::new(Except {
2145                left: self.left,
2146                right: self.right,
2147                all: self.all,
2148                distinct: false,
2149                with: None,
2150                order_by: self.order_by,
2151                limit: self.limit,
2152                offset: self.offset,
2153                distribute_by: None,
2154                sort_by: None,
2155                cluster_by: None,
2156                by_name: false,
2157                side: None,
2158                kind: None,
2159                corresponding: false,
2160                strict: false,
2161                on_columns: Vec::new(),
2162            })),
2163        }
2164    }
2165
2166    /// Consume this builder, generate, and return the SQL string.
2167    pub fn to_sql(self) -> String {
2168        Generator::sql(&self.build()).unwrap_or_default()
2169    }
2170}
2171
2172/// Create a `UNION` (duplicate elimination) of two SELECT queries.
2173///
2174/// Returns a [`SetOpBuilder`] for optional ORDER BY / LIMIT / OFFSET chaining.
2175pub fn union(left: SelectBuilder, right: SelectBuilder) -> SetOpBuilder {
2176    SetOpBuilder::new(SetOpKind::Union, left, right, false)
2177}
2178
2179/// Create a `UNION ALL` (keep duplicates) of two SELECT queries.
2180///
2181/// Returns a [`SetOpBuilder`] for optional ORDER BY / LIMIT / OFFSET chaining.
2182pub fn union_all(left: SelectBuilder, right: SelectBuilder) -> SetOpBuilder {
2183    SetOpBuilder::new(SetOpKind::Union, left, right, true)
2184}
2185
2186/// Create an `INTERSECT` (rows common to both) of two SELECT queries.
2187///
2188/// Returns a [`SetOpBuilder`] for optional ORDER BY / LIMIT / OFFSET chaining.
2189pub fn intersect(left: SelectBuilder, right: SelectBuilder) -> SetOpBuilder {
2190    SetOpBuilder::new(SetOpKind::Intersect, left, right, false)
2191}
2192
2193/// Create an `INTERSECT ALL` (keep duplicate common rows) of two SELECT queries.
2194///
2195/// Returns a [`SetOpBuilder`] for optional ORDER BY / LIMIT / OFFSET chaining.
2196pub fn intersect_all(left: SelectBuilder, right: SelectBuilder) -> SetOpBuilder {
2197    SetOpBuilder::new(SetOpKind::Intersect, left, right, true)
2198}
2199
2200/// Create an `EXCEPT` (rows in left but not right) of two SELECT queries.
2201///
2202/// Returns a [`SetOpBuilder`] for optional ORDER BY / LIMIT / OFFSET chaining.
2203pub fn except_(left: SelectBuilder, right: SelectBuilder) -> SetOpBuilder {
2204    SetOpBuilder::new(SetOpKind::Except, left, right, false)
2205}
2206
2207/// Create an `EXCEPT ALL` (keep duplicate difference rows) of two SELECT queries.
2208///
2209/// Returns a [`SetOpBuilder`] for optional ORDER BY / LIMIT / OFFSET chaining.
2210pub fn except_all(left: SelectBuilder, right: SelectBuilder) -> SetOpBuilder {
2211    SetOpBuilder::new(SetOpKind::Except, left, right, true)
2212}
2213
2214// ---------------------------------------------------------------------------
2215// WindowDefBuilder
2216// ---------------------------------------------------------------------------
2217
2218/// Builder for constructing named `WINDOW` clause definitions.
2219///
2220/// Used with [`SelectBuilder::window()`] to define reusable window specifications.
2221/// Supports PARTITION BY and ORDER BY clauses.
2222///
2223/// # Examples
2224///
2225/// ```
2226/// use polyglot_sql::builder::*;
2227///
2228/// let sql = select(["id"])
2229///     .from("t")
2230///     .window(
2231///         "w",
2232///         WindowDefBuilder::new()
2233///             .partition_by(["dept"])
2234///             .order_by([col("salary").desc()]),
2235///     )
2236///     .to_sql();
2237/// ```
2238pub struct WindowDefBuilder {
2239    partition_by: Vec<Expression>,
2240    order_by: Vec<Ordered>,
2241}
2242
2243impl WindowDefBuilder {
2244    /// Create a new, empty window definition builder with no partitioning or ordering.
2245    pub fn new() -> Self {
2246        WindowDefBuilder {
2247            partition_by: Vec::new(),
2248            order_by: Vec::new(),
2249        }
2250    }
2251
2252    /// Set the PARTITION BY expressions for the window definition.
2253    pub fn partition_by<I, E>(mut self, expressions: I) -> Self
2254    where
2255        I: IntoIterator<Item = E>,
2256        E: IntoExpr,
2257    {
2258        self.partition_by = expressions.into_iter().map(|e| e.into_expr().0).collect();
2259        self
2260    }
2261
2262    /// Set the ORDER BY expressions for the window definition.
2263    ///
2264    /// Expressions not already wrapped with [`.asc()`](Expr::asc) or
2265    /// [`.desc()`](Expr::desc) default to ascending order.
2266    pub fn order_by<I, E>(mut self, expressions: I) -> Self
2267    where
2268        I: IntoIterator<Item = E>,
2269        E: IntoExpr,
2270    {
2271        self.order_by = expressions
2272            .into_iter()
2273            .map(|e| {
2274                let expr = e.into_expr().0;
2275                match expr {
2276                    Expression::Ordered(o) => *o,
2277                    other => Ordered {
2278                        this: other,
2279                        desc: false,
2280                        nulls_first: None,
2281                        explicit_asc: false,
2282                        with_fill: None,
2283                    },
2284                }
2285            })
2286            .collect();
2287        self
2288    }
2289}
2290
2291// ---------------------------------------------------------------------------
2292// Trait: IntoExpr
2293// ---------------------------------------------------------------------------
2294
2295/// Conversion trait for types that can be turned into an [`Expr`].
2296///
2297/// This trait is implemented for:
2298///
2299/// - [`Expr`] -- returned as-is.
2300/// - `&str` and `String` -- converted to a column reference via [`col()`].
2301/// - [`Expression`] -- wrapped directly in an [`Expr`].
2302///
2303/// Note: `&str`/`String` inputs are treated as identifiers, not SQL string
2304/// literals. Use [`lit()`] for literal values.
2305///
2306/// It is used as a generic bound throughout the builder API so that functions like
2307/// [`select()`], [`SelectBuilder::order_by()`], and [`SelectBuilder::group_by()`] can
2308/// accept plain strings, [`Expr`] values, or raw [`Expression`] nodes interchangeably.
2309pub trait IntoExpr {
2310    /// Convert this value into an [`Expr`].
2311    fn into_expr(self) -> Expr;
2312}
2313
2314impl IntoExpr for Expr {
2315    fn into_expr(self) -> Expr {
2316        self
2317    }
2318}
2319
2320impl IntoExpr for &str {
2321    /// Convert a string slice to a column reference via [`col()`].
2322    fn into_expr(self) -> Expr {
2323        col(self)
2324    }
2325}
2326
2327impl IntoExpr for String {
2328    /// Convert an owned string to a column reference via [`col()`].
2329    fn into_expr(self) -> Expr {
2330        col(&self)
2331    }
2332}
2333
2334impl IntoExpr for Expression {
2335    /// Wrap a raw [`Expression`] in an [`Expr`].
2336    fn into_expr(self) -> Expr {
2337        Expr(self)
2338    }
2339}
2340
2341// ---------------------------------------------------------------------------
2342// Trait: IntoLiteral
2343// ---------------------------------------------------------------------------
2344
2345/// Conversion trait for types that can be turned into a SQL literal [`Expr`].
2346///
2347/// This trait is used by [`lit()`] to accept various Rust primitive types and convert
2348/// them into the appropriate SQL literal representation.
2349///
2350/// Implemented for:
2351///
2352/// - `&str`, `String` -- produce a SQL string literal (e.g. `'hello'`).
2353/// - `i32`, `i64`, `usize`, `f64` -- produce a SQL numeric literal (e.g. `42`, `3.14`).
2354/// - `bool` -- produce a SQL boolean literal (`TRUE` or `FALSE`).
2355pub trait IntoLiteral {
2356    /// Convert this value into a literal [`Expr`].
2357    fn into_literal(self) -> Expr;
2358}
2359
2360impl IntoLiteral for &str {
2361    /// Produce a SQL string literal (e.g. `'hello'`).
2362    fn into_literal(self) -> Expr {
2363        Expr(Expression::Literal(Box::new(Literal::String(
2364            self.to_string(),
2365        ))))
2366    }
2367}
2368
2369impl IntoLiteral for String {
2370    /// Produce a SQL string literal from an owned string.
2371    fn into_literal(self) -> Expr {
2372        Expr(Expression::Literal(Box::new(Literal::String(self))))
2373    }
2374}
2375
2376impl IntoLiteral for i64 {
2377    /// Produce a SQL numeric literal from a 64-bit integer.
2378    fn into_literal(self) -> Expr {
2379        Expr(Expression::Literal(Box::new(Literal::Number(
2380            self.to_string(),
2381        ))))
2382    }
2383}
2384
2385impl IntoLiteral for i32 {
2386    /// Produce a SQL numeric literal from a 32-bit integer.
2387    fn into_literal(self) -> Expr {
2388        Expr(Expression::Literal(Box::new(Literal::Number(
2389            self.to_string(),
2390        ))))
2391    }
2392}
2393
2394impl IntoLiteral for usize {
2395    /// Produce a SQL numeric literal from a `usize`.
2396    fn into_literal(self) -> Expr {
2397        Expr(Expression::Literal(Box::new(Literal::Number(
2398            self.to_string(),
2399        ))))
2400    }
2401}
2402
2403impl IntoLiteral for f64 {
2404    /// Produce a SQL numeric literal from a 64-bit float.
2405    fn into_literal(self) -> Expr {
2406        Expr(Expression::Literal(Box::new(Literal::Number(
2407            self.to_string(),
2408        ))))
2409    }
2410}
2411
2412impl IntoLiteral for bool {
2413    /// Produce a SQL boolean literal (`TRUE` or `FALSE`).
2414    fn into_literal(self) -> Expr {
2415        Expr(Expression::Boolean(BooleanLiteral { value: self }))
2416    }
2417}
2418
2419// ---------------------------------------------------------------------------
2420// Helpers
2421// ---------------------------------------------------------------------------
2422
2423fn binary_op(left: Expression, right: Expression) -> BinaryOp {
2424    BinaryOp {
2425        left,
2426        right,
2427        left_comments: Vec::new(),
2428        operator_comments: Vec::new(),
2429        trailing_comments: Vec::new(),
2430        inferred_type: None,
2431    }
2432}
2433
2434// ---------------------------------------------------------------------------
2435// MergeBuilder
2436// ---------------------------------------------------------------------------
2437
2438/// Start building a `MERGE INTO` statement targeting the given table.
2439///
2440/// Returns a [`MergeBuilder`] which supports `.using()`, `.when_matched_update()`,
2441/// `.when_matched_delete()`, and `.when_not_matched_insert()`.
2442///
2443/// # Examples
2444///
2445/// ```
2446/// use polyglot_sql::builder::*;
2447///
2448/// let sql = merge_into("target")
2449///     .using("source", col("target.id").eq(col("source.id")))
2450///     .when_matched_update(vec![("name", col("source.name"))])
2451///     .when_not_matched_insert(&["id", "name"], vec![col("source.id"), col("source.name")])
2452///     .to_sql();
2453/// assert!(sql.contains("MERGE INTO"));
2454/// ```
2455pub fn merge_into(target: &str) -> MergeBuilder {
2456    MergeBuilder {
2457        target: Expression::Table(Box::new(builder_table_ref(target))),
2458        using: None,
2459        on: None,
2460        whens: Vec::new(),
2461    }
2462}
2463
2464/// Fluent builder for constructing `MERGE INTO` statements.
2465///
2466/// Created by the [`merge_into()`] entry-point function.
2467pub struct MergeBuilder {
2468    target: Expression,
2469    using: Option<Expression>,
2470    on: Option<Expression>,
2471    whens: Vec<Expression>,
2472}
2473
2474impl MergeBuilder {
2475    /// Set the source table and ON join condition.
2476    pub fn using(mut self, source: &str, on: Expr) -> Self {
2477        self.using = Some(Expression::Table(Box::new(builder_table_ref(source))));
2478        self.on = Some(on.0);
2479        self
2480    }
2481
2482    /// Add a `WHEN MATCHED THEN UPDATE SET` clause.
2483    pub fn when_matched_update(mut self, assignments: Vec<(&str, Expr)>) -> Self {
2484        let eqs: Vec<Expression> = assignments
2485            .into_iter()
2486            .map(|(col_name, val)| {
2487                Expression::Eq(Box::new(BinaryOp {
2488                    left: Expression::boxed_column(Column {
2489                        name: builder_identifier(col_name),
2490                        table: None,
2491                        join_mark: false,
2492                        trailing_comments: Vec::new(),
2493                        span: None,
2494                        inferred_type: None,
2495                    }),
2496                    right: val.0,
2497                    left_comments: Vec::new(),
2498                    operator_comments: Vec::new(),
2499                    trailing_comments: Vec::new(),
2500                    inferred_type: None,
2501                }))
2502            })
2503            .collect();
2504
2505        let action = Expression::Tuple(Box::new(Tuple {
2506            expressions: vec![
2507                Expression::Var(Box::new(Var {
2508                    this: "UPDATE".to_string(),
2509                })),
2510                Expression::Tuple(Box::new(Tuple { expressions: eqs })),
2511            ],
2512        }));
2513
2514        let when = Expression::When(Box::new(When {
2515            matched: Some(Box::new(Expression::Boolean(BooleanLiteral {
2516                value: true,
2517            }))),
2518            source: None,
2519            condition: None,
2520            then: Box::new(action),
2521        }));
2522        self.whens.push(when);
2523        self
2524    }
2525
2526    /// Add a `WHEN MATCHED THEN UPDATE SET` clause with an additional condition.
2527    pub fn when_matched_update_where(
2528        mut self,
2529        condition: Expr,
2530        assignments: Vec<(&str, Expr)>,
2531    ) -> Self {
2532        let eqs: Vec<Expression> = assignments
2533            .into_iter()
2534            .map(|(col_name, val)| {
2535                Expression::Eq(Box::new(BinaryOp {
2536                    left: Expression::boxed_column(Column {
2537                        name: builder_identifier(col_name),
2538                        table: None,
2539                        join_mark: false,
2540                        trailing_comments: Vec::new(),
2541                        span: None,
2542                        inferred_type: None,
2543                    }),
2544                    right: val.0,
2545                    left_comments: Vec::new(),
2546                    operator_comments: Vec::new(),
2547                    trailing_comments: Vec::new(),
2548                    inferred_type: None,
2549                }))
2550            })
2551            .collect();
2552
2553        let action = Expression::Tuple(Box::new(Tuple {
2554            expressions: vec![
2555                Expression::Var(Box::new(Var {
2556                    this: "UPDATE".to_string(),
2557                })),
2558                Expression::Tuple(Box::new(Tuple { expressions: eqs })),
2559            ],
2560        }));
2561
2562        let when = Expression::When(Box::new(When {
2563            matched: Some(Box::new(Expression::Boolean(BooleanLiteral {
2564                value: true,
2565            }))),
2566            source: None,
2567            condition: Some(Box::new(condition.0)),
2568            then: Box::new(action),
2569        }));
2570        self.whens.push(when);
2571        self
2572    }
2573
2574    /// Add a `WHEN MATCHED THEN DELETE` clause.
2575    pub fn when_matched_delete(mut self) -> Self {
2576        let action = Expression::Var(Box::new(Var {
2577            this: "DELETE".to_string(),
2578        }));
2579
2580        let when = Expression::When(Box::new(When {
2581            matched: Some(Box::new(Expression::Boolean(BooleanLiteral {
2582                value: true,
2583            }))),
2584            source: None,
2585            condition: None,
2586            then: Box::new(action),
2587        }));
2588        self.whens.push(when);
2589        self
2590    }
2591
2592    /// Add a `WHEN NOT MATCHED THEN INSERT (cols) VALUES (vals)` clause.
2593    pub fn when_not_matched_insert(mut self, columns: &[&str], values: Vec<Expr>) -> Self {
2594        let col_exprs: Vec<Expression> = columns
2595            .iter()
2596            .map(|c| {
2597                Expression::boxed_column(Column {
2598                    name: builder_identifier(c),
2599                    table: None,
2600                    join_mark: false,
2601                    trailing_comments: Vec::new(),
2602                    span: None,
2603                    inferred_type: None,
2604                })
2605            })
2606            .collect();
2607        let val_exprs: Vec<Expression> = values.into_iter().map(|v| v.0).collect();
2608
2609        let action = Expression::Tuple(Box::new(Tuple {
2610            expressions: vec![
2611                Expression::Var(Box::new(Var {
2612                    this: "INSERT".to_string(),
2613                })),
2614                Expression::Tuple(Box::new(Tuple {
2615                    expressions: col_exprs,
2616                })),
2617                Expression::Tuple(Box::new(Tuple {
2618                    expressions: val_exprs,
2619                })),
2620            ],
2621        }));
2622
2623        let when = Expression::When(Box::new(When {
2624            matched: Some(Box::new(Expression::Boolean(BooleanLiteral {
2625                value: false,
2626            }))),
2627            source: None,
2628            condition: None,
2629            then: Box::new(action),
2630        }));
2631        self.whens.push(when);
2632        self
2633    }
2634
2635    /// Consume this builder and produce the final [`Expression::Merge`] AST node.
2636    pub fn build(self) -> Expression {
2637        let whens_expr = Expression::Whens(Box::new(Whens {
2638            expressions: self.whens,
2639        }));
2640
2641        Expression::Merge(Box::new(Merge {
2642            this: Box::new(self.target),
2643            using: Box::new(
2644                self.using
2645                    .unwrap_or(Expression::Null(crate::expressions::Null)),
2646            ),
2647            on: self.on.map(Box::new),
2648            using_cond: None,
2649            whens: Some(Box::new(whens_expr)),
2650            with_: None,
2651            returning: None,
2652        }))
2653    }
2654
2655    /// Consume this builder, generate, and return the SQL string.
2656    pub fn to_sql(self) -> String {
2657        Generator::sql(&self.build()).unwrap_or_default()
2658    }
2659}
2660
2661fn parse_simple_data_type(name: &str) -> DataType {
2662    let upper = name.trim().to_uppercase();
2663    match upper.as_str() {
2664        "INT" | "INTEGER" => DataType::Int {
2665            length: None,
2666            integer_spelling: upper == "INTEGER",
2667        },
2668        "BIGINT" => DataType::BigInt { length: None },
2669        "SMALLINT" => DataType::SmallInt { length: None },
2670        "TINYINT" => DataType::TinyInt { length: None },
2671        "FLOAT" => DataType::Float {
2672            precision: None,
2673            scale: None,
2674            real_spelling: false,
2675        },
2676        "DOUBLE" => DataType::Double {
2677            precision: None,
2678            scale: None,
2679        },
2680        "BOOLEAN" | "BOOL" => DataType::Boolean,
2681        "TEXT" => DataType::Text,
2682        "DATE" => DataType::Date,
2683        "TIMESTAMP" => DataType::Timestamp {
2684            precision: None,
2685            timezone: false,
2686        },
2687        "VARCHAR" => DataType::VarChar {
2688            length: None,
2689            parenthesized_length: false,
2690        },
2691        "CHAR" => DataType::Char { length: None },
2692        _ => {
2693            // Try to parse as a full type via the parser for complex types
2694            if let Ok(ast) =
2695                crate::parser::Parser::parse_sql(&format!("SELECT CAST(x AS {})", name))
2696            {
2697                if let Expression::Select(s) = &ast[0] {
2698                    if let Some(Expression::Cast(c)) = s.expressions.first() {
2699                        return c.to.clone();
2700                    }
2701                }
2702            }
2703            // Fallback: treat as a custom type
2704            DataType::Custom {
2705                name: name.to_string(),
2706            }
2707        }
2708    }
2709}
2710
2711#[cfg(test)]
2712mod tests {
2713    use super::*;
2714
2715    #[test]
2716    fn test_simple_select() {
2717        let sql = select(["id", "name"]).from("users").to_sql();
2718        assert_eq!(sql, "SELECT id, name FROM users");
2719    }
2720
2721    #[test]
2722    fn test_builder_quotes_unsafe_identifier_tokens() {
2723        let sql = select(["Name; DROP TABLE titanic"]).to_sql();
2724        assert_eq!(sql, r#"SELECT "Name; DROP TABLE titanic""#);
2725    }
2726
2727    #[test]
2728    fn test_builder_string_literal_requires_lit() {
2729        let sql = select([lit("Name; DROP TABLE titanic")]).to_sql();
2730        assert_eq!(sql, "SELECT 'Name; DROP TABLE titanic'");
2731    }
2732
2733    #[test]
2734    fn test_builder_quotes_unsafe_table_name_tokens() {
2735        let sql = select(["id"]).from("users; DROP TABLE x").to_sql();
2736        assert_eq!(sql, r#"SELECT id FROM "users; DROP TABLE x""#);
2737    }
2738
2739    #[test]
2740    fn test_select_star() {
2741        let sql = select([star()]).from("users").to_sql();
2742        assert_eq!(sql, "SELECT * FROM users");
2743    }
2744
2745    #[test]
2746    fn test_select_with_where() {
2747        let sql = select(["id", "name"])
2748            .from("users")
2749            .where_(col("age").gt(lit(18)))
2750            .to_sql();
2751        assert_eq!(sql, "SELECT id, name FROM users WHERE age > 18");
2752    }
2753
2754    #[test]
2755    fn test_select_with_join() {
2756        let sql = select(["u.id", "o.amount"])
2757            .from("users")
2758            .join("orders", col("u.id").eq(col("o.user_id")))
2759            .to_sql();
2760        assert_eq!(
2761            sql,
2762            "SELECT u.id, o.amount FROM users JOIN orders ON u.id = o.user_id"
2763        );
2764    }
2765
2766    #[test]
2767    fn test_select_with_group_by_having() {
2768        let sql = select([col("dept"), func("COUNT", [star()]).alias("cnt")])
2769            .from("employees")
2770            .group_by(["dept"])
2771            .having(func("COUNT", [star()]).gt(lit(5)))
2772            .to_sql();
2773        assert_eq!(
2774            sql,
2775            "SELECT dept, COUNT(*) AS cnt FROM employees GROUP BY dept HAVING COUNT(*) > 5"
2776        );
2777    }
2778
2779    #[test]
2780    fn test_select_with_order_limit_offset() {
2781        let sql = select(["id", "name"])
2782            .from("users")
2783            .order_by(["name"])
2784            .limit(10)
2785            .offset(20)
2786            .to_sql();
2787        assert_eq!(
2788            sql,
2789            "SELECT id, name FROM users ORDER BY name LIMIT 10 OFFSET 20"
2790        );
2791    }
2792
2793    #[test]
2794    fn test_select_distinct() {
2795        let sql = select(["name"]).from("users").distinct().to_sql();
2796        assert_eq!(sql, "SELECT DISTINCT name FROM users");
2797    }
2798
2799    #[test]
2800    fn test_insert_values() {
2801        let sql = insert_into("users")
2802            .columns(["id", "name"])
2803            .values([lit(1), lit("Alice")])
2804            .values([lit(2), lit("Bob")])
2805            .to_sql();
2806        assert_eq!(
2807            sql,
2808            "INSERT INTO users (id, name) VALUES (1, 'Alice'), (2, 'Bob')"
2809        );
2810    }
2811
2812    #[test]
2813    fn test_insert_select() {
2814        let sql = insert_into("archive")
2815            .columns(["id", "name"])
2816            .query(select(["id", "name"]).from("users"))
2817            .to_sql();
2818        assert_eq!(
2819            sql,
2820            "INSERT INTO archive (id, name) SELECT id, name FROM users"
2821        );
2822    }
2823
2824    #[test]
2825    fn test_update() {
2826        let sql = update("users")
2827            .set("name", lit("Bob"))
2828            .set("age", lit(30))
2829            .where_(col("id").eq(lit(1)))
2830            .to_sql();
2831        assert_eq!(sql, "UPDATE users SET name = 'Bob', age = 30 WHERE id = 1");
2832    }
2833
2834    #[test]
2835    fn test_delete() {
2836        let sql = delete("users").where_(col("id").eq(lit(1))).to_sql();
2837        assert_eq!(sql, "DELETE FROM users WHERE id = 1");
2838    }
2839
2840    #[test]
2841    fn test_complex_where() {
2842        let sql = select(["id"])
2843            .from("users")
2844            .where_(
2845                col("age")
2846                    .gte(lit(18))
2847                    .and(col("active").eq(boolean(true)))
2848                    .and(col("name").like(lit("%test%"))),
2849            )
2850            .to_sql();
2851        assert_eq!(
2852            sql,
2853            "SELECT id FROM users WHERE age >= 18 AND active = TRUE AND name LIKE '%test%'"
2854        );
2855    }
2856
2857    #[test]
2858    fn test_in_list() {
2859        let sql = select(["id"])
2860            .from("users")
2861            .where_(col("status").in_list([lit("active"), lit("pending")]))
2862            .to_sql();
2863        assert_eq!(
2864            sql,
2865            "SELECT id FROM users WHERE status IN ('active', 'pending')"
2866        );
2867    }
2868
2869    #[test]
2870    fn test_between() {
2871        let sql = select(["id"])
2872            .from("orders")
2873            .where_(col("amount").between(lit(100), lit(500)))
2874            .to_sql();
2875        assert_eq!(
2876            sql,
2877            "SELECT id FROM orders WHERE amount BETWEEN 100 AND 500"
2878        );
2879    }
2880
2881    #[test]
2882    fn test_is_null() {
2883        let sql = select(["id"])
2884            .from("users")
2885            .where_(col("email").is_null())
2886            .to_sql();
2887        assert_eq!(sql, "SELECT id FROM users WHERE email IS NULL");
2888    }
2889
2890    #[test]
2891    fn test_arithmetic() {
2892        let sql = select([col("price").mul(col("quantity")).alias("total")])
2893            .from("items")
2894            .to_sql();
2895        assert_eq!(sql, "SELECT price * quantity AS total FROM items");
2896    }
2897
2898    #[test]
2899    fn test_cast() {
2900        let sql = select([col("id").cast("VARCHAR")]).from("users").to_sql();
2901        assert_eq!(sql, "SELECT CAST(id AS VARCHAR) FROM users");
2902    }
2903
2904    #[test]
2905    fn test_from_starter() {
2906        let sql = from("users").select_cols(["id", "name"]).to_sql();
2907        assert_eq!(sql, "SELECT id, name FROM users");
2908    }
2909
2910    #[test]
2911    fn test_qualified_column() {
2912        let sql = select([col("u.id"), col("u.name")]).from("users").to_sql();
2913        assert_eq!(sql, "SELECT u.id, u.name FROM users");
2914    }
2915
2916    #[test]
2917    fn test_nested_dot_column() {
2918        let sql = select([col("t.s.f")]).from("users").to_sql();
2919        assert_eq!(sql, "SELECT t.s.f FROM users");
2920    }
2921
2922    #[test]
2923    fn test_not_condition() {
2924        let sql = select(["id"])
2925            .from("users")
2926            .where_(not(col("active").eq(boolean(true))))
2927            .to_sql();
2928        assert_eq!(sql, "SELECT id FROM users WHERE NOT active = TRUE");
2929    }
2930
2931    #[test]
2932    fn test_order_by_desc() {
2933        let sql = select(["id", "name"])
2934            .from("users")
2935            .order_by([col("name").desc()])
2936            .to_sql();
2937        assert_eq!(sql, "SELECT id, name FROM users ORDER BY name DESC");
2938    }
2939
2940    #[test]
2941    fn test_left_join() {
2942        let sql = select(["u.id", "o.amount"])
2943            .from("users")
2944            .left_join("orders", col("u.id").eq(col("o.user_id")))
2945            .to_sql();
2946        assert_eq!(
2947            sql,
2948            "SELECT u.id, o.amount FROM users LEFT JOIN orders ON u.id = o.user_id"
2949        );
2950    }
2951
2952    #[test]
2953    fn test_build_returns_expression() {
2954        let expr = select(["id"]).from("users").build();
2955        assert!(matches!(expr, Expression::Select(_)));
2956    }
2957
2958    #[test]
2959    fn test_expr_interop() {
2960        // Can use Expr in select list
2961        let age_check = col("age").gt(lit(18));
2962        let sql = select([col("id"), age_check.alias("is_adult")])
2963            .from("users")
2964            .to_sql();
2965        assert_eq!(sql, "SELECT id, age > 18 AS is_adult FROM users");
2966    }
2967
2968    // -- Step 2: sql_expr / condition tests --
2969
2970    #[test]
2971    fn test_sql_expr_simple() {
2972        let expr = sql_expr("age > 18");
2973        let sql = select(["id"]).from("users").where_(expr).to_sql();
2974        assert_eq!(sql, "SELECT id FROM users WHERE age > 18");
2975    }
2976
2977    #[test]
2978    fn test_sql_expr_compound() {
2979        let expr = sql_expr("a > 1 AND b < 10");
2980        let sql = select(["*"]).from("t").where_(expr).to_sql();
2981        assert_eq!(sql, "SELECT * FROM t WHERE a > 1 AND b < 10");
2982    }
2983
2984    #[test]
2985    fn test_sql_expr_function() {
2986        let expr = sql_expr("COALESCE(a, b, 0)");
2987        let sql = select([expr.alias("val")]).from("t").to_sql();
2988        assert_eq!(sql, "SELECT COALESCE(a, b, 0) AS val FROM t");
2989    }
2990
2991    #[test]
2992    fn test_condition_alias() {
2993        let cond = condition("x > 0");
2994        let sql = select(["*"]).from("t").where_(cond).to_sql();
2995        assert_eq!(sql, "SELECT * FROM t WHERE x > 0");
2996    }
2997
2998    // -- Step 3: ilike, rlike, not_in tests --
2999
3000    #[test]
3001    fn test_ilike() {
3002        let sql = select(["id"])
3003            .from("users")
3004            .where_(col("name").ilike(lit("%test%")))
3005            .to_sql();
3006        assert_eq!(sql, "SELECT id FROM users WHERE name ILIKE '%test%'");
3007    }
3008
3009    #[test]
3010    fn test_rlike() {
3011        let sql = select(["id"])
3012            .from("users")
3013            .where_(col("name").rlike(lit("^[A-Z]")))
3014            .to_sql();
3015        assert_eq!(
3016            sql,
3017            "SELECT id FROM users WHERE REGEXP_LIKE(name, '^[A-Z]')"
3018        );
3019    }
3020
3021    #[test]
3022    fn test_not_in() {
3023        let sql = select(["id"])
3024            .from("users")
3025            .where_(col("status").not_in([lit("deleted"), lit("banned")]))
3026            .to_sql();
3027        assert_eq!(
3028            sql,
3029            "SELECT id FROM users WHERE NOT status IN ('deleted', 'banned')"
3030        );
3031    }
3032
3033    // -- Step 4: CaseBuilder tests --
3034
3035    #[test]
3036    fn test_case_searched() {
3037        let expr = case()
3038            .when(col("x").gt(lit(0)), lit("positive"))
3039            .when(col("x").eq(lit(0)), lit("zero"))
3040            .else_(lit("negative"))
3041            .build();
3042        let sql = select([expr.alias("label")]).from("t").to_sql();
3043        assert_eq!(
3044            sql,
3045            "SELECT CASE WHEN x > 0 THEN 'positive' WHEN x = 0 THEN 'zero' ELSE 'negative' END AS label FROM t"
3046        );
3047    }
3048
3049    #[test]
3050    fn test_case_simple() {
3051        let expr = case_of(col("status"))
3052            .when(lit(1), lit("active"))
3053            .when(lit(0), lit("inactive"))
3054            .build();
3055        let sql = select([expr.alias("status_label")]).from("t").to_sql();
3056        assert_eq!(
3057            sql,
3058            "SELECT CASE status WHEN 1 THEN 'active' WHEN 0 THEN 'inactive' END AS status_label FROM t"
3059        );
3060    }
3061
3062    #[test]
3063    fn test_case_no_else() {
3064        let expr = case().when(col("x").gt(lit(0)), lit("yes")).build();
3065        let sql = select([expr]).from("t").to_sql();
3066        assert_eq!(sql, "SELECT CASE WHEN x > 0 THEN 'yes' END FROM t");
3067    }
3068
3069    // -- Step 5: subquery tests --
3070
3071    #[test]
3072    fn test_subquery_in_from() {
3073        let inner = select(["id", "name"])
3074            .from("users")
3075            .where_(col("active").eq(boolean(true)));
3076        let outer = select(["sub.id"])
3077            .from_expr(subquery(inner, "sub"))
3078            .to_sql();
3079        assert_eq!(
3080            outer,
3081            "SELECT sub.id FROM (SELECT id, name FROM users WHERE active = TRUE) AS sub"
3082        );
3083    }
3084
3085    #[test]
3086    fn test_subquery_in_join() {
3087        let inner = select([col("user_id"), func("SUM", [col("amount")]).alias("total")])
3088            .from("orders")
3089            .group_by(["user_id"]);
3090        let sql = select(["u.name", "o.total"])
3091            .from("users")
3092            .join("orders", col("u.id").eq(col("o.user_id")))
3093            .to_sql();
3094        assert!(sql.contains("JOIN"));
3095        // Just verify the subquery builder doesn't panic
3096        let _sub = subquery(inner, "o");
3097    }
3098
3099    // -- Step 6: SetOpBuilder tests --
3100
3101    #[test]
3102    fn test_union() {
3103        let sql = union(select(["id"]).from("a"), select(["id"]).from("b")).to_sql();
3104        assert_eq!(sql, "SELECT id FROM a UNION SELECT id FROM b");
3105    }
3106
3107    #[test]
3108    fn test_union_all() {
3109        let sql = union_all(select(["id"]).from("a"), select(["id"]).from("b")).to_sql();
3110        assert_eq!(sql, "SELECT id FROM a UNION ALL SELECT id FROM b");
3111    }
3112
3113    #[test]
3114    fn test_intersect_builder() {
3115        let sql = intersect(select(["id"]).from("a"), select(["id"]).from("b")).to_sql();
3116        assert_eq!(sql, "SELECT id FROM a INTERSECT SELECT id FROM b");
3117    }
3118
3119    #[test]
3120    fn test_except_builder() {
3121        let sql = except_(select(["id"]).from("a"), select(["id"]).from("b")).to_sql();
3122        assert_eq!(sql, "SELECT id FROM a EXCEPT SELECT id FROM b");
3123    }
3124
3125    #[test]
3126    fn test_union_with_order_limit() {
3127        let sql = union(select(["id"]).from("a"), select(["id"]).from("b"))
3128            .order_by(["id"])
3129            .limit(10)
3130            .to_sql();
3131        assert!(sql.contains("UNION"));
3132        assert!(sql.contains("ORDER BY"));
3133        assert!(sql.contains("LIMIT"));
3134    }
3135
3136    #[test]
3137    fn test_select_builder_union() {
3138        let sql = select(["id"])
3139            .from("a")
3140            .union(select(["id"]).from("b"))
3141            .to_sql();
3142        assert_eq!(sql, "SELECT id FROM a UNION SELECT id FROM b");
3143    }
3144
3145    // -- Step 7: SelectBuilder extensions tests --
3146
3147    #[test]
3148    fn test_qualify() {
3149        let sql = select(["id", "name"])
3150            .from("users")
3151            .qualify(col("rn").eq(lit(1)))
3152            .to_sql();
3153        assert_eq!(sql, "SELECT id, name FROM users QUALIFY rn = 1");
3154    }
3155
3156    #[test]
3157    fn test_right_join() {
3158        let sql = select(["u.id", "o.amount"])
3159            .from("users")
3160            .right_join("orders", col("u.id").eq(col("o.user_id")))
3161            .to_sql();
3162        assert_eq!(
3163            sql,
3164            "SELECT u.id, o.amount FROM users RIGHT JOIN orders ON u.id = o.user_id"
3165        );
3166    }
3167
3168    #[test]
3169    fn test_cross_join() {
3170        let sql = select(["a.x", "b.y"]).from("a").cross_join("b").to_sql();
3171        assert_eq!(sql, "SELECT a.x, b.y FROM a CROSS JOIN b");
3172    }
3173
3174    #[test]
3175    fn test_lateral_view() {
3176        let sql = select(["id", "col_val"])
3177            .from("t")
3178            .lateral_view(func("EXPLODE", [col("arr")]), "lv", ["col_val"])
3179            .to_sql();
3180        assert!(sql.contains("LATERAL VIEW"));
3181        assert!(sql.contains("EXPLODE"));
3182    }
3183
3184    #[test]
3185    fn test_window_clause() {
3186        let sql = select(["id"])
3187            .from("t")
3188            .window(
3189                "w",
3190                WindowDefBuilder::new()
3191                    .partition_by(["dept"])
3192                    .order_by(["salary"]),
3193            )
3194            .to_sql();
3195        assert!(sql.contains("WINDOW"));
3196        assert!(sql.contains("PARTITION BY"));
3197    }
3198
3199    // -- XOR operator tests --
3200
3201    #[test]
3202    fn test_xor() {
3203        let sql = select(["*"])
3204            .from("t")
3205            .where_(col("a").xor(col("b")))
3206            .to_sql();
3207        assert_eq!(sql, "SELECT * FROM t WHERE a XOR b");
3208    }
3209
3210    // -- FOR UPDATE / FOR SHARE tests --
3211
3212    #[test]
3213    fn test_for_update() {
3214        let sql = select(["id"]).from("t").for_update().to_sql();
3215        assert_eq!(sql, "SELECT id FROM t FOR UPDATE");
3216    }
3217
3218    #[test]
3219    fn test_for_share() {
3220        let sql = select(["id"]).from("t").for_share().to_sql();
3221        assert_eq!(sql, "SELECT id FROM t FOR SHARE");
3222    }
3223
3224    // -- Hint tests --
3225
3226    #[test]
3227    fn test_hint() {
3228        let sql = select(["*"]).from("t").hint("FULL(t)").to_sql();
3229        assert!(sql.contains("FULL(t)"), "Expected hint in: {}", sql);
3230    }
3231
3232    // -- CTAS tests --
3233
3234    #[test]
3235    fn test_ctas() {
3236        let expr = select(["*"]).from("t").ctas("new_table");
3237        let sql = Generator::sql(&expr).unwrap();
3238        assert_eq!(sql, "CREATE TABLE new_table AS SELECT * FROM t");
3239    }
3240
3241    // -- MergeBuilder tests --
3242
3243    #[test]
3244    fn test_merge_update_insert() {
3245        let sql = merge_into("target")
3246            .using("source", col("target.id").eq(col("source.id")))
3247            .when_matched_update(vec![("name", col("source.name"))])
3248            .when_not_matched_insert(&["id", "name"], vec![col("source.id"), col("source.name")])
3249            .to_sql();
3250        assert!(
3251            sql.contains("MERGE INTO"),
3252            "Expected MERGE INTO in: {}",
3253            sql
3254        );
3255        assert!(sql.contains("USING"), "Expected USING in: {}", sql);
3256        assert!(
3257            sql.contains("WHEN MATCHED"),
3258            "Expected WHEN MATCHED in: {}",
3259            sql
3260        );
3261        assert!(
3262            sql.contains("UPDATE SET"),
3263            "Expected UPDATE SET in: {}",
3264            sql
3265        );
3266        assert!(
3267            sql.contains("WHEN NOT MATCHED"),
3268            "Expected WHEN NOT MATCHED in: {}",
3269            sql
3270        );
3271        assert!(sql.contains("INSERT"), "Expected INSERT in: {}", sql);
3272    }
3273
3274    #[test]
3275    fn test_merge_delete() {
3276        let sql = merge_into("target")
3277            .using("source", col("target.id").eq(col("source.id")))
3278            .when_matched_delete()
3279            .to_sql();
3280        assert!(
3281            sql.contains("MERGE INTO"),
3282            "Expected MERGE INTO in: {}",
3283            sql
3284        );
3285        assert!(
3286            sql.contains("WHEN MATCHED THEN DELETE"),
3287            "Expected WHEN MATCHED THEN DELETE in: {}",
3288            sql
3289        );
3290    }
3291
3292    #[test]
3293    fn test_merge_with_condition() {
3294        let sql = merge_into("target")
3295            .using("source", col("target.id").eq(col("source.id")))
3296            .when_matched_update_where(
3297                col("source.active").eq(boolean(true)),
3298                vec![("name", col("source.name"))],
3299            )
3300            .to_sql();
3301        assert!(
3302            sql.contains("MERGE INTO"),
3303            "Expected MERGE INTO in: {}",
3304            sql
3305        );
3306        assert!(
3307            sql.contains("AND source.active = TRUE"),
3308            "Expected condition in: {}",
3309            sql
3310        );
3311    }
3312}