Skip to main content

polyglot_sql/
builder.rs

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