Skip to main content

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