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            nulls_auto: false,
1163            this: self.0,
1164            desc: false,
1165            nulls_first: None,
1166            explicit_asc: true,
1167            with_fill: None,
1168        })))
1169    }
1170
1171    /// Wrap this expression with descending sort order (`self DESC`).
1172    ///
1173    /// Used in ORDER BY clauses.
1174    pub fn desc(self) -> Expr {
1175        Expr(Expression::Ordered(Box::new(Ordered {
1176            nulls_auto: false,
1177            this: self.0,
1178            desc: true,
1179            nulls_first: None,
1180            explicit_asc: false,
1181            with_fill: None,
1182        })))
1183    }
1184
1185    /// Produce a `self ILIKE pattern` case-insensitive pattern match.
1186    ///
1187    /// Supported by PostgreSQL, Snowflake, and other dialects. Dialects that do not
1188    /// support `ILIKE` natively may need transpilation.
1189    pub fn ilike(self, pattern: Expr) -> Expr {
1190        Expr(engine::binary(engine::BinaryKind::ILike, self.0, pattern.0))
1191    }
1192
1193    /// Produce a `REGEXP_LIKE(self, pattern)` regular expression match.
1194    ///
1195    /// The generated SQL uses the `REGEXP_LIKE` function form. Different dialects may
1196    /// render this as `RLIKE`, `REGEXP`, or `REGEXP_LIKE` after transpilation.
1197    pub fn rlike(self, pattern: Expr) -> Expr {
1198        Expr(engine::binary(engine::BinaryKind::RLike, self.0, pattern.0))
1199    }
1200
1201    /// Produce a `self NOT IN (values...)` negated membership test.
1202    ///
1203    /// Each element of `values` becomes an item in the parenthesized list.
1204    pub fn not_in(self, values: impl IntoIterator<Item = Expr>) -> Expr {
1205        Expr(Expression::In(Box::new(In {
1206            this: self.0,
1207            expressions: values.into_iter().map(|v| v.0).collect(),
1208            query: None,
1209            not: true,
1210            global: false,
1211            unnest: None,
1212            is_field: false,
1213        })))
1214    }
1215}
1216
1217// ---------------------------------------------------------------------------
1218// SelectBuilder
1219// ---------------------------------------------------------------------------
1220
1221/// Fluent builder for constructing `SELECT` statements.
1222///
1223/// Created by the [`select()`] or [`from()`] entry-point functions. Methods on this
1224/// builder return `self` so they can be chained. Call [`.build()`](SelectBuilder::build)
1225/// to obtain an [`Expression`], or [`.to_sql()`](SelectBuilder::to_sql) to generate a
1226/// SQL string directly.
1227///
1228/// # Examples
1229///
1230/// ```
1231/// use polyglot_sql::builder::*;
1232///
1233/// let sql = select(["u.id", "u.name"])
1234///     .from("users")
1235///     .left_join("orders", col("u.id").eq(col("o.user_id")))
1236///     .where_(col("u.active").eq(boolean(true)))
1237///     .group_by(["u.id", "u.name"])
1238///     .order_by([col("u.name").asc()])
1239///     .limit(100)
1240///     .to_sql();
1241/// ```
1242pub struct SelectBuilder {
1243    select: Select,
1244}
1245
1246impl SelectBuilder {
1247    fn new() -> Self {
1248        SelectBuilder {
1249            select: Select::new(),
1250        }
1251    }
1252
1253    fn edit(mut self, edit: impl FnOnce(&mut Expression)) -> Self {
1254        let mut expression = Expression::Select(Box::new(self.select));
1255        edit(&mut expression);
1256        self.select = match expression {
1257            Expression::Select(select) => *select,
1258            _ => unreachable!("select builder engine changed the expression kind"),
1259        };
1260        self
1261    }
1262
1263    fn join_with_kind(self, table_name: &str, on: Option<Expr>, kind: JoinKind) -> Self {
1264        let join = Join {
1265            kind,
1266            this: Expression::Table(Box::new(builder_table_ref(table_name))),
1267            on: on.map(|expression| expression.0),
1268            using: Vec::new(),
1269            use_inner_keyword: false,
1270            use_outer_keyword: false,
1271            deferred_condition: false,
1272            join_hint: None,
1273            match_condition: None,
1274            pivots: Vec::new(),
1275            comments: Vec::new(),
1276            nesting_group: 0,
1277            directed: false,
1278        };
1279        self.edit(|expression| {
1280            engine::append_join(expression, join).expect("select builder accepts JOIN clauses")
1281        })
1282    }
1283
1284    /// Append columns to the SELECT list.
1285    ///
1286    /// Accepts any iterable of [`IntoExpr`] items. This is primarily useful when the
1287    /// builder was created via [`from()`] and columns need to be added afterward.
1288    pub fn select_cols<I, E>(self, expressions: I) -> Self
1289    where
1290        I: IntoIterator<Item = E>,
1291        E: IntoExpr,
1292    {
1293        self.select_cols_with_options(expressions, ClauseOptions::default())
1294    }
1295
1296    /// Add SELECT expressions, optionally replacing the current projection.
1297    pub fn select_cols_with_options<I, E>(self, expressions: I, options: ClauseOptions) -> Self
1298    where
1299        I: IntoIterator<Item = E>,
1300        E: IntoExpr,
1301    {
1302        let values = expressions
1303            .into_iter()
1304            .map(|expression| expression.into_expr().0)
1305            .collect();
1306        self.edit(|expression| {
1307            engine::append_select(expression, values, options.append)
1308                .expect("select builder accepts SELECT clauses")
1309        })
1310    }
1311
1312    /// Set the FROM clause to reference the given table by name.
1313    pub fn from(self, table_name: &str) -> Self {
1314        self.edit(|expression| {
1315            engine::set_from(
1316                expression,
1317                vec![Expression::Table(Box::new(builder_table_ref(table_name)))],
1318            )
1319            .expect("select builder accepts FROM clauses")
1320        })
1321    }
1322
1323    /// Set the FROM clause to an arbitrary expression (e.g. a subquery or table function).
1324    ///
1325    /// Use this instead of [`SelectBuilder::from()`] when the source is not a simple
1326    /// table name -- for example, a [`subquery()`] or a table-valued function.
1327    pub fn from_expr(self, expr: Expr) -> Self {
1328        self.edit(|expression| {
1329            engine::set_from(expression, vec![expr.0]).expect("select builder accepts FROM clauses")
1330        })
1331    }
1332
1333    /// Add an inner `JOIN` clause with the given ON condition.
1334    pub fn join(self, table_name: &str, on: Expr) -> Self {
1335        self.join_with_kind(table_name, Some(on), JoinKind::Inner)
1336    }
1337
1338    /// Add a `LEFT JOIN` clause with the given ON condition.
1339    pub fn left_join(self, table_name: &str, on: Expr) -> Self {
1340        self.join_with_kind(table_name, Some(on), JoinKind::Left)
1341    }
1342
1343    /// Add a predicate to the WHERE clause, combining repeated calls with `AND`.
1344    pub fn where_(self, condition: Expr) -> Self {
1345        self.where_with_options(condition, ClauseOptions::default())
1346    }
1347
1348    /// Add or replace the WHERE clause according to `options.append`.
1349    pub fn where_with_options(self, condition: Expr, options: ClauseOptions) -> Self {
1350        self.edit(|expression| {
1351            engine::apply_where(expression, condition.0, options.append)
1352                .expect("select builder accepts WHERE clauses")
1353        })
1354    }
1355
1356    /// Set the GROUP BY clause with the given grouping expressions.
1357    pub fn group_by<I, E>(self, expressions: I) -> Self
1358    where
1359        I: IntoIterator<Item = E>,
1360        E: IntoExpr,
1361    {
1362        self.group_by_with_options(expressions, ClauseOptions::default())
1363    }
1364
1365    /// Add or replace grouping expressions according to `options.append`.
1366    pub fn group_by_with_options<I, E>(self, expressions: I, options: ClauseOptions) -> Self
1367    where
1368        I: IntoIterator<Item = E>,
1369        E: IntoExpr,
1370    {
1371        let values = expressions
1372            .into_iter()
1373            .map(|expression| expression.into_expr().0)
1374            .collect();
1375        self.edit(|expression| {
1376            engine::apply_group_by(expression, values, options.append)
1377                .expect("select builder accepts GROUP BY clauses")
1378        })
1379    }
1380
1381    /// Set the HAVING clause to filter groups by the given condition.
1382    pub fn having(self, condition: Expr) -> Self {
1383        self.having_with_options(condition, ClauseOptions::default())
1384    }
1385
1386    /// Add or replace the HAVING clause according to `options.append`.
1387    pub fn having_with_options(self, condition: Expr, options: ClauseOptions) -> Self {
1388        self.edit(|expression| {
1389            engine::apply_having(expression, condition.0, options.append)
1390                .expect("select builder accepts HAVING clauses")
1391        })
1392    }
1393
1394    /// Set the ORDER BY clause with the given sort expressions.
1395    ///
1396    /// Expressions that are not already wrapped with [`.asc()`](Expr::asc) or
1397    /// [`.desc()`](Expr::desc) default to ascending order. String values are
1398    /// interpreted as column names via [`IntoExpr`].
1399    pub fn order_by<I, E>(self, expressions: I) -> Self
1400    where
1401        I: IntoIterator<Item = E>,
1402        E: IntoExpr,
1403    {
1404        self.order_by_with_options(expressions, ClauseOptions::default())
1405    }
1406
1407    /// Add or replace ordering expressions according to `options.append`.
1408    pub fn order_by_with_options<I, E>(self, expressions: I, options: ClauseOptions) -> Self
1409    where
1410        I: IntoIterator<Item = E>,
1411        E: IntoExpr,
1412    {
1413        let values = expressions
1414            .into_iter()
1415            .map(|expression| engine::ordered(expression.into_expr().0))
1416            .collect();
1417        self.edit(|expression| {
1418            engine::apply_order_by(expression, values, options.append)
1419                .expect("select builder accepts ORDER BY clauses")
1420        })
1421    }
1422
1423    /// Set the SORT BY clause with the given sort expressions.
1424    ///
1425    /// SORT BY is used in Hive/Spark to sort data within each reducer (partition),
1426    /// as opposed to ORDER BY which sorts globally. Expressions that are not already
1427    /// wrapped with [`.asc()`](Expr::asc) or [`.desc()`](Expr::desc) default to
1428    /// ascending order.
1429    pub fn sort_by<I, E>(self, expressions: I) -> Self
1430    where
1431        I: IntoIterator<Item = E>,
1432        E: IntoExpr,
1433    {
1434        self.sort_by_with_options(expressions, ClauseOptions::default())
1435    }
1436
1437    /// Add or replace SORT BY expressions according to `options.append`.
1438    pub fn sort_by_with_options<I, E>(self, expressions: I, options: ClauseOptions) -> Self
1439    where
1440        I: IntoIterator<Item = E>,
1441        E: IntoExpr,
1442    {
1443        let values = expressions
1444            .into_iter()
1445            .map(|expression| engine::ordered(expression.into_expr().0))
1446            .collect();
1447        self.edit(|expression| {
1448            engine::apply_sort_by(expression, values, options.append)
1449                .expect("select builder accepts SORT BY clauses")
1450        })
1451    }
1452
1453    /// Set the LIMIT clause to restrict the result set to `count` rows.
1454    pub fn limit(self, count: usize) -> Self {
1455        self.edit(|expression| {
1456            engine::apply_limit(
1457                expression,
1458                Expression::Literal(Box::new(Literal::Number(count.to_string()))),
1459            )
1460            .expect("select builder accepts LIMIT clauses")
1461        })
1462    }
1463
1464    /// Set the OFFSET clause to skip the first `count` rows.
1465    pub fn offset(self, count: usize) -> Self {
1466        self.edit(|expression| {
1467            engine::apply_offset(
1468                expression,
1469                Expression::Literal(Box::new(Literal::Number(count.to_string()))),
1470            )
1471            .expect("select builder accepts OFFSET clauses")
1472        })
1473    }
1474
1475    /// Enable the DISTINCT modifier on the SELECT clause.
1476    pub fn distinct(self) -> Self {
1477        self.edit(|expression| {
1478            engine::apply_distinct(expression, true)
1479                .expect("select builder accepts DISTINCT clauses")
1480        })
1481    }
1482
1483    /// Add a QUALIFY clause to filter rows after window function evaluation.
1484    ///
1485    /// QUALIFY is supported by Snowflake, BigQuery, DuckDB, and Databricks. It acts
1486    /// like a WHERE clause but is applied after window functions are computed.
1487    pub fn qualify(self, condition: Expr) -> Self {
1488        self.qualify_with_options(condition, ClauseOptions::default())
1489    }
1490
1491    /// Add or replace the QUALIFY clause according to `options.append`.
1492    pub fn qualify_with_options(self, condition: Expr, options: ClauseOptions) -> Self {
1493        self.edit(|expression| {
1494            engine::apply_qualify(expression, condition.0, options.append)
1495                .expect("select builder accepts QUALIFY clauses")
1496        })
1497    }
1498
1499    /// Add a `RIGHT JOIN` clause with the given ON condition.
1500    pub fn right_join(self, table_name: &str, on: Expr) -> Self {
1501        self.join_with_kind(table_name, Some(on), JoinKind::Right)
1502    }
1503
1504    /// Add a `FULL JOIN` clause with the given ON condition.
1505    pub fn full_join(self, table_name: &str, on: Expr) -> Self {
1506        self.join_with_kind(table_name, Some(on), JoinKind::Full)
1507    }
1508
1509    /// Add a `CROSS JOIN` clause (Cartesian product, no ON condition).
1510    pub fn cross_join(self, table_name: &str) -> Self {
1511        self.join_with_kind(table_name, None, JoinKind::Cross)
1512    }
1513
1514    /// Add a `LATERAL VIEW` clause for Hive/Spark user-defined table function (UDTF)
1515    /// expansion.
1516    ///
1517    /// `table_function` is the UDTF expression (e.g. `func("EXPLODE", [col("arr")])`),
1518    /// `table_alias` names the virtual table, and `column_aliases` name the output
1519    /// columns produced by the function.
1520    pub fn lateral_view<S: AsRef<str>>(
1521        self,
1522        table_function: Expr,
1523        table_alias: &str,
1524        column_aliases: impl IntoIterator<Item = S>,
1525    ) -> Self {
1526        self.lateral_view_with_options(
1527            table_function,
1528            table_alias,
1529            column_aliases,
1530            LateralViewOptions::default(),
1531        )
1532    }
1533
1534    /// Add a `LATERAL VIEW` clause with options such as `OUTER`.
1535    pub fn lateral_view_with_options<S: AsRef<str>>(
1536        self,
1537        table_function: Expr,
1538        table_alias: &str,
1539        column_aliases: impl IntoIterator<Item = S>,
1540        options: LateralViewOptions,
1541    ) -> Self {
1542        let aliases = column_aliases
1543            .into_iter()
1544            .map(|c| builder_identifier(c.as_ref()))
1545            .collect();
1546        self.edit(|expression| {
1547            engine::append_lateral_view(
1548                expression,
1549                table_function.0,
1550                Some(builder_identifier(table_alias)),
1551                aliases,
1552                options.outer,
1553            )
1554            .expect("select builder accepts LATERAL VIEW clauses")
1555        })
1556    }
1557
1558    /// Add a named `WINDOW` clause definition.
1559    ///
1560    /// The window `name` can then be referenced in window function OVER clauses
1561    /// elsewhere in the query. The definition is constructed via [`WindowDefBuilder`].
1562    /// Multiple calls append additional named windows.
1563    pub fn window(self, name: &str, def: WindowDefBuilder) -> Self {
1564        let order_by = def.order_by;
1565        self.edit(|expression| {
1566            engine::append_window(
1567                expression,
1568                builder_identifier(name),
1569                def.partition_by,
1570                order_by,
1571            )
1572            .expect("select builder accepts WINDOW clauses")
1573        })
1574    }
1575
1576    /// Add a `FOR UPDATE` locking clause.
1577    ///
1578    /// Appends a `FOR UPDATE` lock to the SELECT statement. This is used by
1579    /// databases (PostgreSQL, MySQL, Oracle) to lock selected rows for update.
1580    pub fn for_update(self) -> Self {
1581        self.edit(|expression| {
1582            engine::append_lock(expression, engine::LockKind::Update)
1583                .expect("select builder accepts locking clauses")
1584        })
1585    }
1586
1587    /// Add a `FOR SHARE` locking clause.
1588    ///
1589    /// Appends a `FOR SHARE` lock to the SELECT statement. This allows other
1590    /// transactions to read the locked rows but prevents updates.
1591    pub fn for_share(self) -> Self {
1592        self.edit(|expression| {
1593            engine::append_lock(expression, engine::LockKind::Share)
1594                .expect("select builder accepts locking clauses")
1595        })
1596    }
1597
1598    /// Add a query hint (e.g., Oracle `/*+ FULL(t) */`).
1599    ///
1600    /// Hints are rendered for Oracle, MySQL, Spark, Hive, Databricks, and PostgreSQL
1601    /// dialects. Multiple calls append additional hints.
1602    pub fn hint(self, hint_text: &str) -> Self {
1603        self.edit(|expression| {
1604            engine::append_hint(expression, hint_text.to_string())
1605                .expect("select builder accepts query hints")
1606        })
1607    }
1608
1609    /// Convert this SELECT into a `CREATE TABLE AS SELECT` statement.
1610    ///
1611    /// Consumes the builder and returns an [`Expression::CreateTable`] with this
1612    /// query as the `as_select` source.
1613    ///
1614    /// # Examples
1615    ///
1616    /// ```
1617    /// use polyglot_sql::builder::*;
1618    ///
1619    /// let sql = polyglot_sql::generator::Generator::sql(
1620    ///     &select(["*"]).from("t").ctas("new_table")
1621    /// ).unwrap();
1622    /// assert_eq!(sql, "CREATE TABLE new_table AS SELECT * FROM t");
1623    /// ```
1624    pub fn ctas(self, table_name: &str) -> Expression {
1625        self.ctas_with_options(table_name, CtasOptions::default())
1626    }
1627
1628    /// Build a `CREATE TABLE AS SELECT` statement with replacement/temporary options.
1629    pub fn ctas_with_options(self, table_name: &str, options: CtasOptions) -> Expression {
1630        engine::create_table_as(
1631            self.build(),
1632            builder_table_ref(table_name),
1633            options.replace,
1634            options.temporary,
1635        )
1636        .expect("select builder CTAS source is a query")
1637    }
1638
1639    /// Combine this SELECT with another via `UNION` (duplicate elimination).
1640    ///
1641    /// Returns a [`SetOpBuilder`] for further chaining (e.g. `.order_by()`, `.limit()`).
1642    pub fn union(self, other: SelectBuilder) -> SetOpBuilder {
1643        SetOpBuilder::new(SetOpKind::Union, self, other, false)
1644    }
1645
1646    /// Combine this SELECT with another via `UNION ALL` (keep duplicates).
1647    ///
1648    /// Returns a [`SetOpBuilder`] for further chaining.
1649    pub fn union_all(self, other: SelectBuilder) -> SetOpBuilder {
1650        SetOpBuilder::new(SetOpKind::Union, self, other, true)
1651    }
1652
1653    /// Combine this SELECT with another via `INTERSECT` (rows common to both).
1654    ///
1655    /// Returns a [`SetOpBuilder`] for further chaining.
1656    pub fn intersect(self, other: SelectBuilder) -> SetOpBuilder {
1657        SetOpBuilder::new(SetOpKind::Intersect, self, other, false)
1658    }
1659
1660    /// Combine this SELECT with another via `EXCEPT` (rows in left but not right).
1661    ///
1662    /// Returns a [`SetOpBuilder`] for further chaining.
1663    pub fn except_(self, other: SelectBuilder) -> SetOpBuilder {
1664        SetOpBuilder::new(SetOpKind::Except, self, other, false)
1665    }
1666
1667    /// Consume this builder and produce the final [`Expression::Select`] AST node.
1668    pub fn build(self) -> Expression {
1669        Expression::Select(Box::new(self.select))
1670    }
1671
1672    /// Consume this builder, generate, and return the SQL string.
1673    ///
1674    /// Uses canonical builder rendering (including infix `NOT IN`) and returns an empty
1675    /// string if generation fails.
1676    pub fn to_sql(self) -> String {
1677        generate_builder_sql(&self.build())
1678    }
1679}
1680
1681// ---------------------------------------------------------------------------
1682// DeleteBuilder
1683// ---------------------------------------------------------------------------
1684
1685/// Fluent builder for constructing `DELETE FROM` statements.
1686///
1687/// Created by the [`delete()`] entry-point function. Supports an optional `.where_()`
1688/// predicate.
1689pub struct DeleteBuilder {
1690    delete: Delete,
1691}
1692
1693impl DeleteBuilder {
1694    /// Add a predicate to the WHERE clause, combining repeated calls with `AND`.
1695    pub fn where_(self, condition: Expr) -> Self {
1696        self.where_with_options(condition, ClauseOptions::default())
1697    }
1698
1699    /// Add or replace the WHERE clause according to `options.append`.
1700    pub fn where_with_options(mut self, condition: Expr, options: ClauseOptions) -> Self {
1701        let mut expression = Expression::Delete(Box::new(self.delete));
1702        engine::apply_where(&mut expression, condition.0, options.append)
1703            .expect("delete builder accepts WHERE clauses");
1704        self.delete = match expression {
1705            Expression::Delete(delete) => *delete,
1706            _ => unreachable!("delete builder engine changed the expression kind"),
1707        };
1708        self
1709    }
1710
1711    /// Consume this builder and produce the final [`Expression::Delete`] AST node.
1712    pub fn build(self) -> Expression {
1713        Expression::Delete(Box::new(self.delete))
1714    }
1715
1716    /// Consume this builder, generate, and return the SQL string.
1717    pub fn to_sql(self) -> String {
1718        generate_builder_sql(&self.build())
1719    }
1720}
1721
1722// ---------------------------------------------------------------------------
1723// InsertBuilder
1724// ---------------------------------------------------------------------------
1725
1726/// Fluent builder for constructing `INSERT INTO` statements.
1727///
1728/// Created by the [`insert_into()`] entry-point function. Supports specifying target
1729/// columns via [`.columns()`](InsertBuilder::columns), row values via
1730/// [`.values()`](InsertBuilder::values) (can be called multiple times for multiple rows),
1731/// and INSERT ... SELECT via [`.query()`](InsertBuilder::query).
1732pub struct InsertBuilder {
1733    insert: Insert,
1734}
1735
1736impl InsertBuilder {
1737    fn edit(mut self, edit: impl FnOnce(&mut Expression)) -> Self {
1738        let mut expression = Expression::Insert(Box::new(self.insert));
1739        edit(&mut expression);
1740        self.insert = match expression {
1741            Expression::Insert(insert) => *insert,
1742            _ => unreachable!("insert builder engine changed the expression kind"),
1743        };
1744        self
1745    }
1746
1747    /// Set the target column names for the INSERT statement.
1748    pub fn columns<I, S>(self, columns: I) -> Self
1749    where
1750        I: IntoIterator<Item = S>,
1751        S: AsRef<str>,
1752    {
1753        let columns = columns
1754            .into_iter()
1755            .map(|c| builder_identifier(c.as_ref()))
1756            .collect();
1757        self.edit(|expression| {
1758            engine::set_insert_columns(expression, columns)
1759                .expect("insert builder accepts target columns")
1760        })
1761    }
1762
1763    /// Append a row of values to the VALUES clause.
1764    ///
1765    /// Call this method multiple times to insert multiple rows in a single statement.
1766    pub fn values<I>(self, values: I) -> Self
1767    where
1768        I: IntoIterator<Item = Expr>,
1769    {
1770        let row = values.into_iter().map(|v| v.0).collect();
1771        self.edit(|expression| {
1772            engine::apply_insert_values(expression, vec![row], true)
1773                .expect("insert builder accepts VALUES clauses")
1774        })
1775    }
1776
1777    /// Set the source query for an `INSERT INTO ... SELECT ...` statement.
1778    ///
1779    /// When a query is set, the VALUES clause is ignored during generation.
1780    pub fn query(self, query: SelectBuilder) -> Self {
1781        self.edit(|expression| {
1782            engine::set_insert_query(expression, query.build())
1783                .expect("insert builder accepts query sources")
1784        })
1785    }
1786
1787    /// Consume this builder and produce the final [`Expression::Insert`] AST node.
1788    pub fn build(self) -> Expression {
1789        Expression::Insert(Box::new(self.insert))
1790    }
1791
1792    /// Consume this builder, generate, and return the SQL string.
1793    pub fn to_sql(self) -> String {
1794        generate_builder_sql(&self.build())
1795    }
1796}
1797
1798// ---------------------------------------------------------------------------
1799// UpdateBuilder
1800// ---------------------------------------------------------------------------
1801
1802/// Fluent builder for constructing `UPDATE` statements.
1803///
1804/// Created by the [`update()`] entry-point function. Supports column assignments via
1805/// [`.set()`](UpdateBuilder::set), an optional WHERE predicate, and an optional
1806/// FROM clause for PostgreSQL/Snowflake-style multi-table updates.
1807pub struct UpdateBuilder {
1808    update: Update,
1809}
1810
1811impl UpdateBuilder {
1812    fn edit(mut self, edit: impl FnOnce(&mut Expression)) -> Self {
1813        let mut expression = Expression::Update(Box::new(self.update));
1814        edit(&mut expression);
1815        self.update = match expression {
1816            Expression::Update(update) => *update,
1817            _ => unreachable!("update builder engine changed the expression kind"),
1818        };
1819        self
1820    }
1821
1822    /// Add a `SET column = value` assignment.
1823    ///
1824    /// Call this method multiple times to set multiple columns.
1825    pub fn set(self, column: &str, value: Expr) -> Self {
1826        self.edit(|expression| {
1827            engine::append_update_assignments(
1828                expression,
1829                vec![(builder_identifier(column), value.0)],
1830            )
1831            .expect("update builder accepts SET assignments")
1832        })
1833    }
1834
1835    /// Add a predicate to the WHERE clause, combining repeated calls with `AND`.
1836    pub fn where_(self, condition: Expr) -> Self {
1837        self.where_with_options(condition, ClauseOptions::default())
1838    }
1839
1840    /// Add or replace the WHERE clause according to `options.append`.
1841    pub fn where_with_options(mut self, condition: Expr, options: ClauseOptions) -> Self {
1842        let mut expression = Expression::Update(Box::new(self.update));
1843        engine::apply_where(&mut expression, condition.0, options.append)
1844            .expect("update builder accepts WHERE clauses");
1845        self.update = match expression {
1846            Expression::Update(update) => *update,
1847            _ => unreachable!("update builder engine changed the expression kind"),
1848        };
1849        self
1850    }
1851
1852    /// Set the FROM clause for PostgreSQL/Snowflake-style `UPDATE ... FROM ...` syntax.
1853    ///
1854    /// This allows joining against other tables within the UPDATE statement.
1855    pub fn from(self, table_name: &str) -> Self {
1856        self.edit(|expression| {
1857            engine::set_from(
1858                expression,
1859                vec![Expression::Table(Box::new(builder_table_ref(table_name)))],
1860            )
1861            .expect("update builder accepts FROM clauses")
1862        })
1863    }
1864
1865    /// Consume this builder and produce the final [`Expression::Update`] AST node.
1866    pub fn build(self) -> Expression {
1867        Expression::Update(Box::new(self.update))
1868    }
1869
1870    /// Consume this builder, generate, and return the SQL string.
1871    pub fn to_sql(self) -> String {
1872        generate_builder_sql(&self.build())
1873    }
1874}
1875
1876// ---------------------------------------------------------------------------
1877// CaseBuilder
1878// ---------------------------------------------------------------------------
1879
1880/// Start building a searched CASE expression (`CASE WHEN cond THEN result ... END`).
1881///
1882/// A searched CASE evaluates each WHEN condition independently. Use [`case_of()`] for
1883/// a simple CASE that compares an operand against values.
1884///
1885/// # Examples
1886///
1887/// ```
1888/// use polyglot_sql::builder::*;
1889///
1890/// let expr = case()
1891///     .when(col("x").gt(lit(0)), lit("positive"))
1892///     .when(col("x").eq(lit(0)), lit("zero"))
1893///     .else_(lit("negative"))
1894///     .build();
1895/// assert_eq!(
1896///     expr.to_sql(),
1897///     "CASE WHEN x > 0 THEN 'positive' WHEN x = 0 THEN 'zero' ELSE 'negative' END"
1898/// );
1899/// ```
1900pub fn case() -> CaseBuilder {
1901    CaseBuilder {
1902        operand: None,
1903        whens: Vec::new(),
1904        else_: None,
1905    }
1906}
1907
1908/// Start building a simple CASE expression (`CASE operand WHEN value THEN result ... END`).
1909///
1910/// A simple CASE compares the `operand` against each WHEN value for equality. Use
1911/// [`case()`] for a searched CASE with arbitrary boolean conditions.
1912///
1913/// # Examples
1914///
1915/// ```
1916/// use polyglot_sql::builder::*;
1917///
1918/// let expr = case_of(col("status"))
1919///     .when(lit(1), lit("active"))
1920///     .when(lit(0), lit("inactive"))
1921///     .else_(lit("unknown"))
1922///     .build();
1923/// assert_eq!(
1924///     expr.to_sql(),
1925///     "CASE status WHEN 1 THEN 'active' WHEN 0 THEN 'inactive' ELSE 'unknown' END"
1926/// );
1927/// ```
1928pub fn case_of(operand: Expr) -> CaseBuilder {
1929    CaseBuilder {
1930        operand: Some(operand.0),
1931        whens: Vec::new(),
1932        else_: None,
1933    }
1934}
1935
1936/// Fluent builder for SQL `CASE` expressions (both searched and simple forms).
1937///
1938/// Created by [`case()`] (searched form) or [`case_of()`] (simple form). Add branches
1939/// with [`.when()`](CaseBuilder::when) and an optional default with
1940/// [`.else_()`](CaseBuilder::else_). Finalize with [`.build()`](CaseBuilder::build) to
1941/// get an [`Expr`], or [`.build_expr()`](CaseBuilder::build_expr) for a raw
1942/// [`Expression`].
1943pub struct CaseBuilder {
1944    operand: Option<Expression>,
1945    whens: Vec<(Expression, Expression)>,
1946    else_: Option<Expression>,
1947}
1948
1949impl CaseBuilder {
1950    /// Add a `WHEN condition THEN result` branch to the CASE expression.
1951    ///
1952    /// For a searched CASE ([`case()`]), `condition` is a boolean predicate. For a simple
1953    /// CASE ([`case_of()`]), `condition` is the value to compare against the operand.
1954    pub fn when(mut self, condition: Expr, result: Expr) -> Self {
1955        self.whens.push((condition.0, result.0));
1956        self
1957    }
1958
1959    /// Set the `ELSE result` default branch of the CASE expression.
1960    ///
1961    /// If not called, the CASE expression has no ELSE clause (implicitly NULL when
1962    /// no WHEN matches).
1963    pub fn else_(mut self, result: Expr) -> Self {
1964        self.else_ = Some(result.0);
1965        self
1966    }
1967
1968    /// Consume this builder and produce an [`Expr`] wrapping the CASE expression.
1969    pub fn build(self) -> Expr {
1970        Expr(self.build_expr())
1971    }
1972
1973    /// Consume this builder and produce the raw [`Expression::Case`] AST node.
1974    ///
1975    /// Use this instead of [`.build()`](CaseBuilder::build) when you need the
1976    /// [`Expression`] directly rather than an [`Expr`] wrapper.
1977    pub fn build_expr(self) -> Expression {
1978        let mut expression = engine::case(self.operand);
1979        for (condition, result) in self.whens {
1980            engine::append_case_when(&mut expression, condition, result)
1981                .expect("case builder accepts WHEN branches");
1982        }
1983        if let Some(result) = self.else_ {
1984            engine::set_case_else(&mut expression, result)
1985                .expect("case builder accepts ELSE branches");
1986        }
1987        expression
1988    }
1989}
1990
1991// ---------------------------------------------------------------------------
1992// Subquery builders
1993// ---------------------------------------------------------------------------
1994
1995/// Wrap a [`SelectBuilder`] as a named subquery for use in FROM or JOIN clauses.
1996///
1997/// The resulting [`Expr`] can be passed to [`SelectBuilder::from_expr()`] or used
1998/// in a join condition.
1999///
2000/// # Examples
2001///
2002/// ```
2003/// use polyglot_sql::builder::*;
2004///
2005/// let inner = select(["id", "name"]).from("users").where_(col("active").eq(boolean(true)));
2006/// let sql = select(["sub.id"])
2007///     .from_expr(subquery(inner, "sub"))
2008///     .to_sql();
2009/// assert_eq!(
2010///     sql,
2011///     "SELECT sub.id FROM (SELECT id, name FROM users WHERE active = TRUE) AS sub"
2012/// );
2013/// ```
2014pub fn subquery(query: SelectBuilder, alias_name: &str) -> Expr {
2015    subquery_expr(query.build(), alias_name)
2016}
2017
2018/// Wrap an existing [`Expression`] as a named subquery.
2019///
2020/// This is the lower-level version of [`subquery()`] that accepts a pre-built
2021/// [`Expression`] instead of a [`SelectBuilder`].
2022pub fn subquery_expr(expr: Expression, alias_name: &str) -> Expr {
2023    Expr(
2024        engine::subquery(expr, Some(builder_identifier(alias_name)), true)
2025            .expect("subquery builder source is a query"),
2026    )
2027}
2028
2029// ---------------------------------------------------------------------------
2030// SetOpBuilder
2031// ---------------------------------------------------------------------------
2032
2033/// Internal enum distinguishing the three kinds of set operations.
2034#[derive(Debug, Clone, Copy)]
2035enum SetOpKind {
2036    Union,
2037    Intersect,
2038    Except,
2039}
2040
2041/// Fluent builder for `UNION`, `INTERSECT`, and `EXCEPT` set operations.
2042///
2043/// Created by the free functions [`union()`], [`union_all()`], [`intersect()`],
2044/// [`intersect_all()`], [`except_()`], [`except_all()`], or the corresponding methods
2045/// on [`SelectBuilder`]. Supports optional `.order_by()`, `.limit()`, and `.offset()`
2046/// clauses applied to the combined result.
2047///
2048/// # Examples
2049///
2050/// ```
2051/// use polyglot_sql::builder::*;
2052///
2053/// let sql = union_all(
2054///     select(["id"]).from("a"),
2055///     select(["id"]).from("b"),
2056/// )
2057/// .order_by(["id"])
2058/// .limit(10)
2059/// .to_sql();
2060/// ```
2061pub struct SetOpBuilder {
2062    kind: SetOpKind,
2063    left: Expression,
2064    right: Expression,
2065    all: bool,
2066    order_by: Option<OrderBy>,
2067    limit: Option<Box<Expression>>,
2068    offset: Option<Box<Expression>>,
2069}
2070
2071impl SetOpBuilder {
2072    fn new(kind: SetOpKind, left: SelectBuilder, right: SelectBuilder, all: bool) -> Self {
2073        SetOpBuilder {
2074            kind,
2075            left: left.build(),
2076            right: right.build(),
2077            all,
2078            order_by: None,
2079            limit: None,
2080            offset: None,
2081        }
2082    }
2083
2084    /// Add an ORDER BY clause applied to the combined set operation result.
2085    ///
2086    /// Expressions not already wrapped with [`.asc()`](Expr::asc) or
2087    /// [`.desc()`](Expr::desc) default to ascending order.
2088    pub fn order_by<I, E>(self, expressions: I) -> Self
2089    where
2090        I: IntoIterator<Item = E>,
2091        E: IntoExpr,
2092    {
2093        self.order_by_with_options(expressions, ClauseOptions::default())
2094    }
2095
2096    /// Add or replace ordering expressions according to `options.append`.
2097    pub fn order_by_with_options<I, E>(mut self, expressions: I, options: ClauseOptions) -> Self
2098    where
2099        I: IntoIterator<Item = E>,
2100        E: IntoExpr,
2101    {
2102        let values: Vec<_> = expressions
2103            .into_iter()
2104            .map(|expression| engine::ordered(expression.into_expr().0))
2105            .collect();
2106        if options.append {
2107            self.order_by
2108                .get_or_insert_with(|| OrderBy {
2109                    siblings: false,
2110                    comments: Vec::new(),
2111                    expressions: Vec::new(),
2112                })
2113                .expressions
2114                .extend(values);
2115        } else {
2116            self.order_by = Some(OrderBy {
2117                siblings: false,
2118                comments: Vec::new(),
2119                expressions: values,
2120            });
2121        }
2122        self
2123    }
2124
2125    /// Restrict the combined set operation result to `count` rows.
2126    pub fn limit(mut self, count: usize) -> Self {
2127        self.limit = Some(Box::new(Expression::Literal(Box::new(Literal::Number(
2128            count.to_string(),
2129        )))));
2130        self
2131    }
2132
2133    /// Skip the first `count` rows from the combined set operation result.
2134    pub fn offset(mut self, count: usize) -> Self {
2135        self.offset = Some(Box::new(Expression::Literal(Box::new(Literal::Number(
2136            count.to_string(),
2137        )))));
2138        self
2139    }
2140
2141    /// Consume this builder and produce the final set operation [`Expression`] AST node.
2142    ///
2143    /// The returned expression is one of [`Expression::Union`], [`Expression::Intersect`],
2144    /// or [`Expression::Except`] depending on how the builder was created.
2145    pub fn build(self) -> Expression {
2146        let kind = match self.kind {
2147            SetOpKind::Union => engine::SetKind::Union,
2148            SetOpKind::Intersect => engine::SetKind::Intersect,
2149            SetOpKind::Except => engine::SetKind::Except,
2150        };
2151        let mut expression = engine::set_operation(kind, self.left, self.right, !self.all)
2152            .expect("set builder operands are queries");
2153        if let Some(order_by) = self.order_by {
2154            engine::apply_order_by(&mut expression, order_by.expressions, false)
2155                .expect("set operations accept ORDER BY clauses");
2156        }
2157        if let Some(limit) = self.limit {
2158            engine::apply_limit(&mut expression, *limit)
2159                .expect("set operations accept LIMIT clauses");
2160        }
2161        if let Some(offset) = self.offset {
2162            engine::apply_offset(&mut expression, *offset)
2163                .expect("set operations accept OFFSET clauses");
2164        }
2165        expression
2166    }
2167
2168    /// Consume this builder, generate, and return the SQL string.
2169    pub fn to_sql(self) -> String {
2170        generate_builder_sql(&self.build())
2171    }
2172}
2173
2174/// Create a `UNION` (duplicate elimination) of two SELECT queries.
2175///
2176/// Returns a [`SetOpBuilder`] for optional ORDER BY / LIMIT / OFFSET chaining.
2177pub fn union(left: SelectBuilder, right: SelectBuilder) -> SetOpBuilder {
2178    SetOpBuilder::new(SetOpKind::Union, left, right, false)
2179}
2180
2181/// Create a `UNION ALL` (keep duplicates) of two SELECT queries.
2182///
2183/// Returns a [`SetOpBuilder`] for optional ORDER BY / LIMIT / OFFSET chaining.
2184pub fn union_all(left: SelectBuilder, right: SelectBuilder) -> SetOpBuilder {
2185    SetOpBuilder::new(SetOpKind::Union, left, right, true)
2186}
2187
2188/// Create an `INTERSECT` (rows common to both) of two SELECT queries.
2189///
2190/// Returns a [`SetOpBuilder`] for optional ORDER BY / LIMIT / OFFSET chaining.
2191pub fn intersect(left: SelectBuilder, right: SelectBuilder) -> SetOpBuilder {
2192    SetOpBuilder::new(SetOpKind::Intersect, left, right, false)
2193}
2194
2195/// Create an `INTERSECT ALL` (keep duplicate common rows) of two SELECT queries.
2196///
2197/// Returns a [`SetOpBuilder`] for optional ORDER BY / LIMIT / OFFSET chaining.
2198pub fn intersect_all(left: SelectBuilder, right: SelectBuilder) -> SetOpBuilder {
2199    SetOpBuilder::new(SetOpKind::Intersect, left, right, true)
2200}
2201
2202/// Create an `EXCEPT` (rows in left but not right) of two SELECT queries.
2203///
2204/// Returns a [`SetOpBuilder`] for optional ORDER BY / LIMIT / OFFSET chaining.
2205pub fn except_(left: SelectBuilder, right: SelectBuilder) -> SetOpBuilder {
2206    SetOpBuilder::new(SetOpKind::Except, left, right, false)
2207}
2208
2209/// Create an `EXCEPT ALL` (keep duplicate difference rows) of two SELECT queries.
2210///
2211/// Returns a [`SetOpBuilder`] for optional ORDER BY / LIMIT / OFFSET chaining.
2212pub fn except_all(left: SelectBuilder, right: SelectBuilder) -> SetOpBuilder {
2213    SetOpBuilder::new(SetOpKind::Except, left, right, true)
2214}
2215
2216// ---------------------------------------------------------------------------
2217// WindowDefBuilder
2218// ---------------------------------------------------------------------------
2219
2220/// Builder for constructing named `WINDOW` clause definitions.
2221///
2222/// Used with [`SelectBuilder::window()`] to define reusable window specifications.
2223/// Supports PARTITION BY and ORDER BY clauses.
2224///
2225/// # Examples
2226///
2227/// ```
2228/// use polyglot_sql::builder::*;
2229///
2230/// let sql = select(["id"])
2231///     .from("t")
2232///     .window(
2233///         "w",
2234///         WindowDefBuilder::new()
2235///             .partition_by(["dept"])
2236///             .order_by([col("salary").desc()]),
2237///     )
2238///     .to_sql();
2239/// ```
2240pub struct WindowDefBuilder {
2241    partition_by: Vec<Expression>,
2242    order_by: Vec<Ordered>,
2243}
2244
2245impl WindowDefBuilder {
2246    /// Create a new, empty window definition builder with no partitioning or ordering.
2247    pub fn new() -> Self {
2248        WindowDefBuilder {
2249            partition_by: Vec::new(),
2250            order_by: Vec::new(),
2251        }
2252    }
2253
2254    /// Set the PARTITION BY expressions for the window definition.
2255    pub fn partition_by<I, E>(mut self, expressions: I) -> Self
2256    where
2257        I: IntoIterator<Item = E>,
2258        E: IntoExpr,
2259    {
2260        self.partition_by = expressions.into_iter().map(|e| e.into_expr().0).collect();
2261        self
2262    }
2263
2264    /// Set the ORDER BY expressions for the window definition.
2265    ///
2266    /// Expressions not already wrapped with [`.asc()`](Expr::asc) or
2267    /// [`.desc()`](Expr::desc) default to ascending order.
2268    pub fn order_by<I, E>(mut self, expressions: I) -> Self
2269    where
2270        I: IntoIterator<Item = E>,
2271        E: IntoExpr,
2272    {
2273        self.order_by = expressions
2274            .into_iter()
2275            .map(|e| {
2276                let expr = e.into_expr().0;
2277                match expr {
2278                    Expression::Ordered(o) => *o,
2279                    other => Ordered {
2280                        nulls_auto: false,
2281                        this: other,
2282                        desc: false,
2283                        nulls_first: None,
2284                        explicit_asc: false,
2285                        with_fill: None,
2286                    },
2287                }
2288            })
2289            .collect();
2290        self
2291    }
2292}
2293
2294// ---------------------------------------------------------------------------
2295// Trait: IntoExpr
2296// ---------------------------------------------------------------------------
2297
2298/// Conversion trait for types that can be turned into an [`Expr`].
2299///
2300/// This trait is implemented for:
2301///
2302/// - [`Expr`] -- returned as-is.
2303/// - `&str` and `String` -- converted to a column reference via [`col()`].
2304/// - [`Expression`] -- wrapped directly in an [`Expr`].
2305///
2306/// Note: `&str`/`String` inputs are treated as identifiers, not SQL string
2307/// literals. Use [`lit()`] for literal values.
2308///
2309/// It is used as a generic bound throughout the builder API so that functions like
2310/// [`select()`], [`SelectBuilder::order_by()`], and [`SelectBuilder::group_by()`] can
2311/// accept plain strings, [`Expr`] values, or raw [`Expression`] nodes interchangeably.
2312pub trait IntoExpr {
2313    /// Convert this value into an [`Expr`].
2314    fn into_expr(self) -> Expr;
2315}
2316
2317impl IntoExpr for Expr {
2318    fn into_expr(self) -> Expr {
2319        self
2320    }
2321}
2322
2323impl IntoExpr for &str {
2324    /// Convert a string slice to a column reference via [`col()`].
2325    fn into_expr(self) -> Expr {
2326        col(self)
2327    }
2328}
2329
2330impl IntoExpr for String {
2331    /// Convert an owned string to a column reference via [`col()`].
2332    fn into_expr(self) -> Expr {
2333        col(&self)
2334    }
2335}
2336
2337impl IntoExpr for Expression {
2338    /// Wrap a raw [`Expression`] in an [`Expr`].
2339    fn into_expr(self) -> Expr {
2340        Expr(self)
2341    }
2342}
2343
2344// ---------------------------------------------------------------------------
2345// Trait: IntoLiteral
2346// ---------------------------------------------------------------------------
2347
2348/// Conversion trait for types that can be turned into a SQL literal [`Expr`].
2349///
2350/// This trait is used by [`lit()`] to accept various Rust primitive types and convert
2351/// them into the appropriate SQL literal representation.
2352///
2353/// Implemented for:
2354///
2355/// - `&str`, `String` -- produce a SQL string literal (e.g. `'hello'`).
2356/// - `i32`, `i64`, `usize`, `f64` -- produce a SQL numeric literal (e.g. `42`, `3.14`).
2357/// - `bool` -- produce a SQL boolean literal (`TRUE` or `FALSE`).
2358pub trait IntoLiteral {
2359    /// Convert this value into a literal [`Expr`].
2360    fn into_literal(self) -> Expr;
2361}
2362
2363impl IntoLiteral for &str {
2364    /// Produce a SQL string literal (e.g. `'hello'`).
2365    fn into_literal(self) -> Expr {
2366        Expr(Expression::Literal(Box::new(Literal::String(
2367            self.to_string(),
2368        ))))
2369    }
2370}
2371
2372impl IntoLiteral for String {
2373    /// Produce a SQL string literal from an owned string.
2374    fn into_literal(self) -> Expr {
2375        Expr(Expression::Literal(Box::new(Literal::String(self))))
2376    }
2377}
2378
2379impl IntoLiteral for i64 {
2380    /// Produce a SQL numeric literal from a 64-bit integer.
2381    fn into_literal(self) -> Expr {
2382        Expr(Expression::Literal(Box::new(Literal::Number(
2383            self.to_string(),
2384        ))))
2385    }
2386}
2387
2388impl IntoLiteral for i32 {
2389    /// Produce a SQL numeric literal from a 32-bit integer.
2390    fn into_literal(self) -> Expr {
2391        Expr(Expression::Literal(Box::new(Literal::Number(
2392            self.to_string(),
2393        ))))
2394    }
2395}
2396
2397impl IntoLiteral for usize {
2398    /// Produce a SQL numeric literal from a `usize`.
2399    fn into_literal(self) -> Expr {
2400        Expr(Expression::Literal(Box::new(Literal::Number(
2401            self.to_string(),
2402        ))))
2403    }
2404}
2405
2406impl IntoLiteral for f64 {
2407    /// Produce a SQL numeric literal from a 64-bit float.
2408    fn into_literal(self) -> Expr {
2409        Expr(Expression::Literal(Box::new(Literal::Number(
2410            self.to_string(),
2411        ))))
2412    }
2413}
2414
2415impl IntoLiteral for bool {
2416    /// Produce a SQL boolean literal (`TRUE` or `FALSE`).
2417    fn into_literal(self) -> Expr {
2418        Expr(Expression::Boolean(BooleanLiteral { value: self }))
2419    }
2420}
2421
2422// ---------------------------------------------------------------------------
2423// MergeBuilder
2424// ---------------------------------------------------------------------------
2425
2426/// Start building a `MERGE INTO` statement targeting the given table.
2427///
2428/// Returns a [`MergeBuilder`] which supports `.using()`, `.when_matched_update()`,
2429/// `.when_matched_delete()`, and `.when_not_matched_insert()`.
2430///
2431/// # Examples
2432///
2433/// ```
2434/// use polyglot_sql::builder::*;
2435///
2436/// let sql = merge_into("target")
2437///     .using("source", col("target.id").eq(col("source.id")))
2438///     .when_matched_update(vec![("name", col("source.name"))])
2439///     .when_not_matched_insert(&["id", "name"], vec![col("source.id"), col("source.name")])
2440///     .to_sql();
2441/// assert!(sql.contains("MERGE INTO"));
2442/// ```
2443pub fn merge_into(target: &str) -> MergeBuilder {
2444    MergeBuilder {
2445        expression: engine::merge(Expression::Table(Box::new(builder_table_ref(target)))),
2446    }
2447}
2448
2449/// Fluent builder for constructing `MERGE INTO` statements.
2450///
2451/// Created by the [`merge_into()`] entry-point function.
2452pub struct MergeBuilder {
2453    expression: Expression,
2454}
2455
2456impl MergeBuilder {
2457    /// Set the source table and ON join condition.
2458    pub fn using(mut self, source: &str, on: Expr) -> Self {
2459        engine::set_merge_using(
2460            &mut self.expression,
2461            Expression::Table(Box::new(builder_table_ref(source))),
2462            on.0,
2463        )
2464        .expect("merge builder accepts a USING clause");
2465        self
2466    }
2467
2468    /// Add a `WHEN MATCHED THEN UPDATE SET` clause.
2469    pub fn when_matched_update(mut self, assignments: Vec<(&str, Expr)>) -> Self {
2470        let assignments = assignments
2471            .into_iter()
2472            .map(|(column, value)| (builder_identifier(column), value.0))
2473            .collect();
2474        engine::append_merge_update(&mut self.expression, assignments, None)
2475            .expect("merge builder accepts matched update actions");
2476        self
2477    }
2478
2479    /// Add a `WHEN MATCHED THEN UPDATE SET` clause with an additional condition.
2480    pub fn when_matched_update_where(
2481        mut self,
2482        condition: Expr,
2483        assignments: Vec<(&str, Expr)>,
2484    ) -> Self {
2485        let assignments = assignments
2486            .into_iter()
2487            .map(|(column, value)| (builder_identifier(column), value.0))
2488            .collect();
2489        engine::append_merge_update(&mut self.expression, assignments, Some(condition.0))
2490            .expect("merge builder accepts conditional matched update actions");
2491        self
2492    }
2493
2494    /// Add a `WHEN MATCHED THEN DELETE` clause.
2495    pub fn when_matched_delete(mut self) -> Self {
2496        engine::append_merge_delete(&mut self.expression, None)
2497            .expect("merge builder accepts matched delete actions");
2498        self
2499    }
2500
2501    /// Add a conditional `WHEN MATCHED THEN DELETE` clause.
2502    pub fn when_matched_delete_where(mut self, condition: Expr) -> Self {
2503        engine::append_merge_delete(&mut self.expression, Some(condition.0))
2504            .expect("merge builder accepts conditional matched delete actions");
2505        self
2506    }
2507
2508    /// Add a `WHEN NOT MATCHED THEN INSERT (cols) VALUES (vals)` clause.
2509    pub fn when_not_matched_insert(mut self, columns: &[&str], values: Vec<Expr>) -> Self {
2510        engine::append_merge_insert(
2511            &mut self.expression,
2512            columns
2513                .iter()
2514                .map(|column| builder_identifier(column))
2515                .collect(),
2516            values.into_iter().map(|value| value.0).collect(),
2517            None,
2518        )
2519        .expect("merge builder accepts not-matched insert actions");
2520        self
2521    }
2522
2523    /// Add a conditional `WHEN NOT MATCHED THEN INSERT` clause.
2524    pub fn when_not_matched_insert_where(
2525        mut self,
2526        condition: Expr,
2527        columns: &[&str],
2528        values: Vec<Expr>,
2529    ) -> Self {
2530        engine::append_merge_insert(
2531            &mut self.expression,
2532            columns
2533                .iter()
2534                .map(|column| builder_identifier(column))
2535                .collect(),
2536            values.into_iter().map(|value| value.0).collect(),
2537            Some(condition.0),
2538        )
2539        .expect("merge builder accepts conditional not-matched insert actions");
2540        self
2541    }
2542
2543    /// Consume this builder and produce the final [`Expression::Merge`] AST node.
2544    pub fn build(self) -> Expression {
2545        self.expression
2546    }
2547
2548    /// Consume this builder, generate, and return the SQL string.
2549    pub fn to_sql(self) -> String {
2550        generate_builder_sql(&self.build())
2551    }
2552}
2553
2554fn parse_simple_data_type(name: &str) -> DataType {
2555    let upper = name.trim().to_uppercase();
2556    match upper.as_str() {
2557        "INT" | "INTEGER" => DataType::Int {
2558            length: None,
2559            integer_spelling: upper == "INTEGER",
2560        },
2561        "BIGINT" => DataType::BigInt { length: None },
2562        "SMALLINT" => DataType::SmallInt { length: None },
2563        "TINYINT" => DataType::TinyInt { length: None },
2564        "FLOAT" => DataType::Float {
2565            precision: None,
2566            scale: None,
2567            real_spelling: false,
2568        },
2569        "DOUBLE" => DataType::Double {
2570            precision: None,
2571            scale: None,
2572        },
2573        "BOOLEAN" | "BOOL" => DataType::Boolean,
2574        "TEXT" => DataType::Text,
2575        "DATE" => DataType::Date,
2576        "TIMESTAMP" => DataType::Timestamp {
2577            precision: None,
2578            timezone: false,
2579        },
2580        "VARCHAR" => DataType::VarChar {
2581            length: None,
2582            parenthesized_length: false,
2583        },
2584        "CHAR" => DataType::Char { length: None },
2585        _ => {
2586            // Try to parse as a full type via the parser for complex types
2587            if let Ok(ast) =
2588                crate::parser::Parser::parse_sql(&format!("SELECT CAST(x AS {})", name))
2589            {
2590                if let Expression::Select(s) = &ast[0] {
2591                    if let Some(Expression::Cast(c)) = s.expressions.first() {
2592                        return c.to.clone();
2593                    }
2594                }
2595            }
2596            // Fallback: treat as a custom type
2597            DataType::Custom {
2598                name: name.to_string(),
2599            }
2600        }
2601    }
2602}
2603
2604#[cfg(test)]
2605mod tests {
2606    use super::*;
2607
2608    #[test]
2609    fn test_simple_select() {
2610        let sql = select(["id", "name"]).from("users").to_sql();
2611        assert_eq!(sql, "SELECT id, name FROM users");
2612    }
2613
2614    #[test]
2615    fn test_builder_quotes_unsafe_identifier_tokens() {
2616        let sql = select(["Name; DROP TABLE titanic"]).to_sql();
2617        assert_eq!(sql, r#"SELECT "Name; DROP TABLE titanic""#);
2618    }
2619
2620    #[test]
2621    fn test_builder_string_literal_requires_lit() {
2622        let sql = select([lit("Name; DROP TABLE titanic")]).to_sql();
2623        assert_eq!(sql, "SELECT 'Name; DROP TABLE titanic'");
2624    }
2625
2626    #[test]
2627    fn test_builder_quotes_unsafe_table_name_tokens() {
2628        let sql = select(["id"]).from("users; DROP TABLE x").to_sql();
2629        assert_eq!(sql, r#"SELECT id FROM "users; DROP TABLE x""#);
2630    }
2631
2632    #[test]
2633    fn test_select_star() {
2634        let sql = select([star()]).from("users").to_sql();
2635        assert_eq!(sql, "SELECT * FROM users");
2636    }
2637
2638    #[test]
2639    fn test_select_with_where() {
2640        let sql = select(["id", "name"])
2641            .from("users")
2642            .where_(col("age").gt(lit(18)))
2643            .to_sql();
2644        assert_eq!(sql, "SELECT id, name FROM users WHERE age > 18");
2645    }
2646
2647    #[test]
2648    fn test_select_with_join() {
2649        let sql = select(["u.id", "o.amount"])
2650            .from("users")
2651            .join("orders", col("u.id").eq(col("o.user_id")))
2652            .to_sql();
2653        assert_eq!(
2654            sql,
2655            "SELECT u.id, o.amount FROM users JOIN orders ON u.id = o.user_id"
2656        );
2657    }
2658
2659    #[test]
2660    fn test_select_with_group_by_having() {
2661        let sql = select([col("dept"), func("COUNT", [star()]).alias("cnt")])
2662            .from("employees")
2663            .group_by(["dept"])
2664            .having(func("COUNT", [star()]).gt(lit(5)))
2665            .to_sql();
2666        assert_eq!(
2667            sql,
2668            "SELECT dept, COUNT(*) AS cnt FROM employees GROUP BY dept HAVING COUNT(*) > 5"
2669        );
2670    }
2671
2672    #[test]
2673    fn test_select_with_order_limit_offset() {
2674        let sql = select(["id", "name"])
2675            .from("users")
2676            .order_by(["name"])
2677            .limit(10)
2678            .offset(20)
2679            .to_sql();
2680        assert_eq!(
2681            sql,
2682            "SELECT id, name FROM users ORDER BY name LIMIT 10 OFFSET 20"
2683        );
2684    }
2685
2686    #[test]
2687    fn test_select_distinct() {
2688        let sql = select(["name"]).from("users").distinct().to_sql();
2689        assert_eq!(sql, "SELECT DISTINCT name FROM users");
2690    }
2691
2692    #[test]
2693    fn test_insert_values() {
2694        let sql = insert_into("users")
2695            .columns(["id", "name"])
2696            .values([lit(1), lit("Alice")])
2697            .values([lit(2), lit("Bob")])
2698            .to_sql();
2699        assert_eq!(
2700            sql,
2701            "INSERT INTO users (id, name) VALUES (1, 'Alice'), (2, 'Bob')"
2702        );
2703    }
2704
2705    #[test]
2706    fn test_insert_select() {
2707        let sql = insert_into("archive")
2708            .columns(["id", "name"])
2709            .query(select(["id", "name"]).from("users"))
2710            .to_sql();
2711        assert_eq!(
2712            sql,
2713            "INSERT INTO archive (id, name) SELECT id, name FROM users"
2714        );
2715    }
2716
2717    #[test]
2718    fn test_update() {
2719        let sql = update("users")
2720            .set("name", lit("Bob"))
2721            .set("age", lit(30))
2722            .where_(col("id").eq(lit(1)))
2723            .to_sql();
2724        assert_eq!(sql, "UPDATE users SET name = 'Bob', age = 30 WHERE id = 1");
2725    }
2726
2727    #[test]
2728    fn test_delete() {
2729        let sql = delete("users").where_(col("id").eq(lit(1))).to_sql();
2730        assert_eq!(sql, "DELETE FROM users WHERE id = 1");
2731    }
2732
2733    #[test]
2734    fn test_complex_where() {
2735        let sql = select(["id"])
2736            .from("users")
2737            .where_(
2738                col("age")
2739                    .gte(lit(18))
2740                    .and(col("active").eq(boolean(true)))
2741                    .and(col("name").like(lit("%test%"))),
2742            )
2743            .to_sql();
2744        assert_eq!(
2745            sql,
2746            "SELECT id FROM users WHERE age >= 18 AND active = TRUE AND name LIKE '%test%'"
2747        );
2748    }
2749
2750    #[test]
2751    fn test_in_list() {
2752        let sql = select(["id"])
2753            .from("users")
2754            .where_(col("status").in_list([lit("active"), lit("pending")]))
2755            .to_sql();
2756        assert_eq!(
2757            sql,
2758            "SELECT id FROM users WHERE status IN ('active', 'pending')"
2759        );
2760    }
2761
2762    #[test]
2763    fn test_between() {
2764        let sql = select(["id"])
2765            .from("orders")
2766            .where_(col("amount").between(lit(100), lit(500)))
2767            .to_sql();
2768        assert_eq!(
2769            sql,
2770            "SELECT id FROM orders WHERE amount BETWEEN 100 AND 500"
2771        );
2772    }
2773
2774    #[test]
2775    fn test_is_null() {
2776        let sql = select(["id"])
2777            .from("users")
2778            .where_(col("email").is_null())
2779            .to_sql();
2780        assert_eq!(sql, "SELECT id FROM users WHERE email IS NULL");
2781    }
2782
2783    #[test]
2784    fn test_arithmetic() {
2785        let sql = select([col("price").mul(col("quantity")).alias("total")])
2786            .from("items")
2787            .to_sql();
2788        assert_eq!(sql, "SELECT price * quantity AS total FROM items");
2789    }
2790
2791    #[test]
2792    fn test_nested_operator_grouping() {
2793        assert_eq!(
2794            or(col("a").eq(lit(1)), col("b").eq(lit(2)))
2795                .and(col("c").eq(lit(3)))
2796                .to_sql(),
2797            "(a = 1 OR b = 2) AND c = 3"
2798        );
2799        assert_eq!(
2800            col("a")
2801                .eq(lit(1))
2802                .and(or(col("b").eq(lit(2)), col("c").eq(lit(3))))
2803                .to_sql(),
2804            "a = 1 AND (b = 2 OR c = 3)"
2805        );
2806        assert_eq!(col("a").add(col("b")).mul(col("c")).to_sql(), "(a + b) * c");
2807        assert_eq!(col("a").mul(col("b").add(col("c"))).to_sql(), "a * (b + c)");
2808        assert_eq!(col("a").sub(col("b").sub(col("c"))).to_sql(), "a - (b - c)");
2809        assert_eq!(col("a").div(col("b").div(col("c"))).to_sql(), "a / (b / c)");
2810    }
2811
2812    #[test]
2813    fn test_cast() {
2814        let sql = select([col("id").cast("VARCHAR")]).from("users").to_sql();
2815        assert_eq!(sql, "SELECT CAST(id AS VARCHAR) FROM users");
2816    }
2817
2818    #[test]
2819    fn test_cast_int128() {
2820        for name in ["HUGEINT", "INT128"] {
2821            let Expression::Cast(cast) = col("value").cast(name).0 else {
2822                panic!("expected cast");
2823            };
2824            assert_eq!(cast.to, DataType::Int128);
2825        }
2826    }
2827
2828    #[test]
2829    fn test_cast_unsigned_integer_types() {
2830        for (name, expected) in [
2831            ("UTINYINT", DataType::UInt8),
2832            ("UINT8", DataType::UInt8),
2833            ("USMALLINT", DataType::UInt16),
2834            ("UINT16", DataType::UInt16),
2835            ("UINTEGER", DataType::UInt32),
2836            ("UINT32", DataType::UInt32),
2837            ("UBIGINT", DataType::UInt64),
2838            ("UINT64", DataType::UInt64),
2839            ("UHUGEINT", DataType::UInt128),
2840            ("UINT128", DataType::UInt128),
2841        ] {
2842            let Expression::Cast(cast) = col("value").cast(name).0 else {
2843                panic!("expected cast")
2844            };
2845            assert_eq!(cast.to, expected, "{name}");
2846        }
2847    }
2848
2849    #[test]
2850    fn test_from_starter() {
2851        let sql = from("users").select_cols(["id", "name"]).to_sql();
2852        assert_eq!(sql, "SELECT id, name FROM users");
2853    }
2854
2855    #[test]
2856    fn test_qualified_column() {
2857        let sql = select([col("u.id"), col("u.name")]).from("users").to_sql();
2858        assert_eq!(sql, "SELECT u.id, u.name FROM users");
2859    }
2860
2861    #[test]
2862    fn test_nested_dot_column() {
2863        let sql = select([col("t.s.f")]).from("users").to_sql();
2864        assert_eq!(sql, "SELECT t.s.f FROM users");
2865    }
2866
2867    #[test]
2868    fn test_not_condition() {
2869        let sql = select(["id"])
2870            .from("users")
2871            .where_(not(col("active").eq(boolean(true))))
2872            .to_sql();
2873        assert_eq!(sql, "SELECT id FROM users WHERE NOT active = TRUE");
2874    }
2875
2876    #[test]
2877    fn test_order_by_desc() {
2878        let sql = select(["id", "name"])
2879            .from("users")
2880            .order_by([col("name").desc()])
2881            .to_sql();
2882        assert_eq!(sql, "SELECT id, name FROM users ORDER BY name DESC");
2883    }
2884
2885    #[test]
2886    fn test_left_join() {
2887        let sql = select(["u.id", "o.amount"])
2888            .from("users")
2889            .left_join("orders", col("u.id").eq(col("o.user_id")))
2890            .to_sql();
2891        assert_eq!(
2892            sql,
2893            "SELECT u.id, o.amount FROM users LEFT JOIN orders ON u.id = o.user_id"
2894        );
2895    }
2896
2897    #[test]
2898    fn test_build_returns_expression() {
2899        let expr = select(["id"]).from("users").build();
2900        assert!(matches!(expr, Expression::Select(_)));
2901    }
2902
2903    #[test]
2904    fn test_expr_interop() {
2905        // Can use Expr in select list
2906        let age_check = col("age").gt(lit(18));
2907        let sql = select([col("id"), age_check.alias("is_adult")])
2908            .from("users")
2909            .to_sql();
2910        assert_eq!(sql, "SELECT id, age > 18 AS is_adult FROM users");
2911    }
2912
2913    // -- Step 2: sql_expr / condition tests --
2914
2915    #[test]
2916    fn test_sql_expr_simple() {
2917        let expr = sql_expr("age > 18");
2918        let sql = select(["id"]).from("users").where_(expr).to_sql();
2919        assert_eq!(sql, "SELECT id FROM users WHERE age > 18");
2920    }
2921
2922    #[test]
2923    fn test_sql_expr_compound() {
2924        let expr = sql_expr("a > 1 AND b < 10");
2925        let sql = select(["*"]).from("t").where_(expr).to_sql();
2926        assert_eq!(sql, "SELECT * FROM t WHERE a > 1 AND b < 10");
2927    }
2928
2929    #[test]
2930    fn test_sql_expr_function() {
2931        let expr = sql_expr("COALESCE(a, b, 0)");
2932        let sql = select([expr.alias("val")]).from("t").to_sql();
2933        assert_eq!(sql, "SELECT COALESCE(a, b, 0) AS val FROM t");
2934    }
2935
2936    #[test]
2937    fn test_condition_alias() {
2938        let cond = condition("x > 0");
2939        let sql = select(["*"]).from("t").where_(cond).to_sql();
2940        assert_eq!(sql, "SELECT * FROM t WHERE x > 0");
2941    }
2942
2943    // -- Step 3: ilike, rlike, not_in tests --
2944
2945    #[test]
2946    fn test_ilike() {
2947        let sql = select(["id"])
2948            .from("users")
2949            .where_(col("name").ilike(lit("%test%")))
2950            .to_sql();
2951        assert_eq!(sql, "SELECT id FROM users WHERE name ILIKE '%test%'");
2952    }
2953
2954    #[test]
2955    fn test_rlike() {
2956        let sql = select(["id"])
2957            .from("users")
2958            .where_(col("name").rlike(lit("^[A-Z]")))
2959            .to_sql();
2960        assert_eq!(
2961            sql,
2962            "SELECT id FROM users WHERE REGEXP_LIKE(name, '^[A-Z]')"
2963        );
2964    }
2965
2966    #[test]
2967    fn test_not_in() {
2968        let sql = select(["id"])
2969            .from("users")
2970            .where_(col("status").not_in([lit("deleted"), lit("banned")]))
2971            .to_sql();
2972        assert_eq!(
2973            sql,
2974            "SELECT id FROM users WHERE status NOT IN ('deleted', 'banned')"
2975        );
2976    }
2977
2978    #[test]
2979    fn repeated_clauses_append_unless_replacement_is_requested() {
2980        let appended = select(["x"])
2981            .where_(col("x").gt(lit(0)))
2982            .where_(col("x").lt(lit(10)))
2983            .group_by(["x"])
2984            .group_by(["y"])
2985            .to_sql();
2986        assert_eq!(appended, "SELECT x WHERE x > 0 AND x < 10 GROUP BY x, y");
2987
2988        let replaced = select(["x"])
2989            .where_(col("x").gt(lit(0)))
2990            .where_with_options(col("x").eq(lit(5)), ClauseOptions { append: false })
2991            .group_by(["x"])
2992            .group_by_with_options(["y"], ClauseOptions { append: false })
2993            .to_sql();
2994        assert_eq!(replaced, "SELECT x WHERE x = 5 GROUP BY y");
2995    }
2996
2997    // -- Step 4: CaseBuilder tests --
2998
2999    #[test]
3000    fn test_case_searched() {
3001        let expr = case()
3002            .when(col("x").gt(lit(0)), lit("positive"))
3003            .when(col("x").eq(lit(0)), lit("zero"))
3004            .else_(lit("negative"))
3005            .build();
3006        let sql = select([expr.alias("label")]).from("t").to_sql();
3007        assert_eq!(
3008            sql,
3009            "SELECT CASE WHEN x > 0 THEN 'positive' WHEN x = 0 THEN 'zero' ELSE 'negative' END AS label FROM t"
3010        );
3011    }
3012
3013    #[test]
3014    fn test_case_simple() {
3015        let expr = case_of(col("status"))
3016            .when(lit(1), lit("active"))
3017            .when(lit(0), lit("inactive"))
3018            .build();
3019        let sql = select([expr.alias("status_label")]).from("t").to_sql();
3020        assert_eq!(
3021            sql,
3022            "SELECT CASE status WHEN 1 THEN 'active' WHEN 0 THEN 'inactive' END AS status_label FROM t"
3023        );
3024    }
3025
3026    #[test]
3027    fn test_case_no_else() {
3028        let expr = case().when(col("x").gt(lit(0)), lit("yes")).build();
3029        let sql = select([expr]).from("t").to_sql();
3030        assert_eq!(sql, "SELECT CASE WHEN x > 0 THEN 'yes' END FROM t");
3031    }
3032
3033    // -- Step 5: subquery tests --
3034
3035    #[test]
3036    fn test_subquery_in_from() {
3037        let inner = select(["id", "name"])
3038            .from("users")
3039            .where_(col("active").eq(boolean(true)));
3040        let outer = select(["sub.id"])
3041            .from_expr(subquery(inner, "sub"))
3042            .to_sql();
3043        assert_eq!(
3044            outer,
3045            "SELECT sub.id FROM (SELECT id, name FROM users WHERE active = TRUE) AS sub"
3046        );
3047    }
3048
3049    #[test]
3050    fn test_subquery_in_join() {
3051        let inner = select([col("user_id"), func("SUM", [col("amount")]).alias("total")])
3052            .from("orders")
3053            .group_by(["user_id"]);
3054        let sql = select(["u.name", "o.total"])
3055            .from("users")
3056            .join("orders", col("u.id").eq(col("o.user_id")))
3057            .to_sql();
3058        assert!(sql.contains("JOIN"));
3059        // Just verify the subquery builder doesn't panic
3060        let _sub = subquery(inner, "o");
3061    }
3062
3063    // -- Step 6: SetOpBuilder tests --
3064
3065    #[test]
3066    fn test_union() {
3067        let sql = union(select(["id"]).from("a"), select(["id"]).from("b")).to_sql();
3068        assert_eq!(sql, "SELECT id FROM a UNION SELECT id FROM b");
3069    }
3070
3071    #[test]
3072    fn test_union_all() {
3073        let sql = union_all(select(["id"]).from("a"), select(["id"]).from("b")).to_sql();
3074        assert_eq!(sql, "SELECT id FROM a UNION ALL SELECT id FROM b");
3075    }
3076
3077    #[test]
3078    fn test_intersect_builder() {
3079        let sql = intersect(select(["id"]).from("a"), select(["id"]).from("b")).to_sql();
3080        assert_eq!(sql, "SELECT id FROM a INTERSECT SELECT id FROM b");
3081    }
3082
3083    #[test]
3084    fn test_except_builder() {
3085        let sql = except_(select(["id"]).from("a"), select(["id"]).from("b")).to_sql();
3086        assert_eq!(sql, "SELECT id FROM a EXCEPT SELECT id FROM b");
3087    }
3088
3089    #[test]
3090    fn test_union_with_order_limit() {
3091        let sql = union(select(["id"]).from("a"), select(["id"]).from("b"))
3092            .order_by(["id"])
3093            .limit(10)
3094            .to_sql();
3095        assert!(sql.contains("UNION"));
3096        assert!(sql.contains("ORDER BY"));
3097        assert!(sql.contains("LIMIT"));
3098    }
3099
3100    #[test]
3101    fn test_select_builder_union() {
3102        let sql = select(["id"])
3103            .from("a")
3104            .union(select(["id"]).from("b"))
3105            .to_sql();
3106        assert_eq!(sql, "SELECT id FROM a UNION SELECT id FROM b");
3107    }
3108
3109    // -- Step 7: SelectBuilder extensions tests --
3110
3111    #[test]
3112    fn test_qualify() {
3113        let sql = select(["id", "name"])
3114            .from("users")
3115            .qualify(col("rn").eq(lit(1)))
3116            .to_sql();
3117        assert_eq!(sql, "SELECT id, name FROM users QUALIFY rn = 1");
3118    }
3119
3120    #[test]
3121    fn test_right_join() {
3122        let sql = select(["u.id", "o.amount"])
3123            .from("users")
3124            .right_join("orders", col("u.id").eq(col("o.user_id")))
3125            .to_sql();
3126        assert_eq!(
3127            sql,
3128            "SELECT u.id, o.amount FROM users RIGHT JOIN orders ON u.id = o.user_id"
3129        );
3130    }
3131
3132    #[test]
3133    fn test_cross_join() {
3134        let sql = select(["a.x", "b.y"]).from("a").cross_join("b").to_sql();
3135        assert_eq!(sql, "SELECT a.x, b.y FROM a CROSS JOIN b");
3136    }
3137
3138    #[test]
3139    fn test_lateral_view() {
3140        let sql = select(["id", "col_val"])
3141            .from("t")
3142            .lateral_view(func("EXPLODE", [col("arr")]), "lv", ["col_val"])
3143            .to_sql();
3144        assert!(sql.contains("LATERAL VIEW"));
3145        assert!(sql.contains("EXPLODE"));
3146
3147        let expression = select(["id"])
3148            .from("t")
3149            .lateral_view_with_options(
3150                func("EXPLODE", [col("arr")]),
3151                "lv",
3152                ["value"],
3153                LateralViewOptions { outer: true },
3154            )
3155            .build();
3156        let Expression::Select(select) = expression else {
3157            panic!("expected SELECT")
3158        };
3159        assert!(select.lateral_views[0].outer);
3160    }
3161
3162    #[test]
3163    fn test_window_clause() {
3164        let sql = select(["id"])
3165            .from("t")
3166            .window(
3167                "w",
3168                WindowDefBuilder::new()
3169                    .partition_by(["dept"])
3170                    .order_by(["salary"]),
3171            )
3172            .to_sql();
3173        assert!(sql.contains("WINDOW"));
3174        assert!(sql.contains("PARTITION BY"));
3175    }
3176
3177    // -- XOR operator tests --
3178
3179    #[test]
3180    fn test_xor() {
3181        let sql = select(["*"])
3182            .from("t")
3183            .where_(col("a").xor(col("b")))
3184            .to_sql();
3185        assert_eq!(sql, "SELECT * FROM t WHERE a XOR b");
3186    }
3187
3188    // -- FOR UPDATE / FOR SHARE tests --
3189
3190    #[test]
3191    fn test_for_update() {
3192        let sql = select(["id"]).from("t").for_update().to_sql();
3193        assert_eq!(sql, "SELECT id FROM t FOR UPDATE");
3194    }
3195
3196    #[test]
3197    fn test_for_share() {
3198        let sql = select(["id"]).from("t").for_share().to_sql();
3199        assert_eq!(sql, "SELECT id FROM t FOR SHARE");
3200    }
3201
3202    // -- Hint tests --
3203
3204    #[test]
3205    fn test_hint() {
3206        let sql = select(["*"]).from("t").hint("FULL(t)").to_sql();
3207        assert!(sql.contains("FULL(t)"), "Expected hint in: {}", sql);
3208    }
3209
3210    // -- CTAS tests --
3211
3212    #[test]
3213    fn test_ctas() {
3214        let expr = select(["*"]).from("t").ctas("new_table");
3215        let sql = Generator::sql(&expr).unwrap();
3216        assert_eq!(sql, "CREATE TABLE new_table AS SELECT * FROM t");
3217
3218        let expr = select(["*"]).from("t").ctas_with_options(
3219            "new_table",
3220            CtasOptions {
3221                replace: true,
3222                temporary: true,
3223            },
3224        );
3225        let Expression::CreateTable(create) = expr else {
3226            panic!("expected CREATE TABLE")
3227        };
3228        assert!(create.or_replace);
3229        assert!(create.temporary);
3230    }
3231
3232    // -- MergeBuilder tests --
3233
3234    #[test]
3235    fn test_merge_update_insert() {
3236        let sql = merge_into("target")
3237            .using("source", col("target.id").eq(col("source.id")))
3238            .when_matched_update(vec![("name", col("source.name"))])
3239            .when_not_matched_insert(&["id", "name"], vec![col("source.id"), col("source.name")])
3240            .to_sql();
3241        assert!(
3242            sql.contains("MERGE INTO"),
3243            "Expected MERGE INTO in: {}",
3244            sql
3245        );
3246        assert!(sql.contains("USING"), "Expected USING in: {}", sql);
3247        assert!(
3248            sql.contains("WHEN MATCHED"),
3249            "Expected WHEN MATCHED in: {}",
3250            sql
3251        );
3252        assert!(
3253            sql.contains("UPDATE SET"),
3254            "Expected UPDATE SET in: {}",
3255            sql
3256        );
3257        assert!(
3258            sql.contains("WHEN NOT MATCHED"),
3259            "Expected WHEN NOT MATCHED in: {}",
3260            sql
3261        );
3262        assert!(sql.contains("INSERT"), "Expected INSERT in: {}", sql);
3263    }
3264
3265    #[test]
3266    fn test_merge_delete() {
3267        let sql = merge_into("target")
3268            .using("source", col("target.id").eq(col("source.id")))
3269            .when_matched_delete()
3270            .to_sql();
3271        assert!(
3272            sql.contains("MERGE INTO"),
3273            "Expected MERGE INTO in: {}",
3274            sql
3275        );
3276        assert!(
3277            sql.contains("WHEN MATCHED THEN DELETE"),
3278            "Expected WHEN MATCHED THEN DELETE in: {}",
3279            sql
3280        );
3281    }
3282
3283    #[test]
3284    fn test_merge_with_condition() {
3285        let sql = merge_into("target")
3286            .using("source", col("target.id").eq(col("source.id")))
3287            .when_matched_update_where(
3288                col("source.active").eq(boolean(true)),
3289                vec![("name", col("source.name"))],
3290            )
3291            .to_sql();
3292        assert!(
3293            sql.contains("MERGE INTO"),
3294            "Expected MERGE INTO in: {}",
3295            sql
3296        );
3297        assert!(
3298            sql.contains("AND source.active = TRUE"),
3299            "Expected condition in: {}",
3300            sql
3301        );
3302    }
3303}