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