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