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