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