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