Skip to main content

sqlglot_rust/builder/
mod.rs

1//! # Expression Builder API
2//!
3//! Fluent builder API for programmatic SQL construction and manipulation.
4//!
5//! This module provides ergonomic builders inspired by Python sqlglot's
6//! builder API, allowing construction of SQL expressions without manual
7//! AST enum construction.
8//!
9//! ## Quick Start
10//!
11//! ```rust
12//! use sqlglot_rust::builder::{select, column, literal, condition};
13//! use sqlglot_rust::{Dialect, generate};
14//!
15//! // Build a SELECT query fluently
16//! let query = select(&["a", "b"])
17//!     .from("users")
18//!     .where_clause("active = true")
19//!     .order_by(&["created_at"])
20//!     .limit(10)
21//!     .build();
22//!
23//! let sql = generate(&query, Dialect::Postgres);
24//! ```
25//!
26//! ## Condition Builder
27//!
28//! ```rust
29//! use sqlglot_rust::builder::condition;
30//!
31//! // Build complex conditions
32//! let cond = condition("x = 1")
33//!     .and("y = 2")
34//!     .or("z = 3")
35//!     .build();
36//! ```
37//!
38//! ## Expression Factory Functions
39//!
40//! ```rust
41//! use sqlglot_rust::builder::{column, table, literal, cast, and_all, or_all};
42//! use sqlglot_rust::ast::DataType;
43//!
44//! // Create expressions directly
45//! let col = column("name", Some("users"));
46//! let tbl = table("users", Some("public"));
47//! let num = literal(42);
48//! let casted = cast(column("id", None), DataType::BigInt);
49//! ```
50
51use crate::ast::{
52    BinaryOperator, DataType, Expr, FromClause, JoinClause, JoinType, OrderByItem, QuoteStyle,
53    SelectItem, SelectStatement, Statement, TableRef, TableSource,
54};
55use crate::dialects::Dialect;
56use crate::parser::parse;
57
58// ═══════════════════════════════════════════════════════════════════════
59// Expression Factory Functions
60// ═══════════════════════════════════════════════════════════════════════
61
62/// Create a column expression.
63///
64/// # Arguments
65/// * `name` - Column name
66/// * `table` - Optional table qualifier
67///
68/// # Examples
69///
70/// ```rust
71/// use sqlglot_rust::builder::column;
72///
73/// let col = column("id", None);
74/// let qualified = column("name", Some("users"));
75/// ```
76#[must_use]
77pub fn column(name: &str, table: Option<&str>) -> Expr {
78    Expr::Column {
79        table: table.map(String::from),
80        name: name.to_string(),
81        quote_style: QuoteStyle::None,
82        table_quote_style: QuoteStyle::None,
83    }
84}
85
86/// Create a table reference.
87///
88/// # Arguments
89/// * `name` - Table name
90/// * `schema` - Optional schema qualifier
91///
92/// # Examples
93///
94/// ```rust
95/// use sqlglot_rust::builder::table;
96///
97/// let tbl = table("users", None);
98/// let qualified = table("orders", Some("public"));
99/// ```
100#[must_use]
101pub fn table(name: &str, schema: Option<&str>) -> TableRef {
102    TableRef {
103        catalog: None,
104        schema: schema.map(String::from),
105        name: name.to_string(),
106        alias: None,
107        name_quote_style: QuoteStyle::None,
108        alias_quote_style: QuoteStyle::None,
109    }
110}
111
112/// Create a fully qualified table reference with catalog.
113///
114/// # Arguments
115/// * `name` - Table name
116/// * `schema` - Optional schema qualifier
117/// * `catalog` - Optional catalog qualifier
118///
119/// # Examples
120///
121/// ```rust
122/// use sqlglot_rust::builder::table_full;
123///
124/// let tbl = table_full("users", Some("public"), Some("mydb"));
125/// ```
126#[must_use]
127pub fn table_full(name: &str, schema: Option<&str>, catalog: Option<&str>) -> TableRef {
128    TableRef {
129        catalog: catalog.map(String::from),
130        schema: schema.map(String::from),
131        name: name.to_string(),
132        alias: None,
133        name_quote_style: QuoteStyle::None,
134        alias_quote_style: QuoteStyle::None,
135    }
136}
137
138/// Create an integer literal expression.
139///
140/// # Examples
141///
142/// ```rust
143/// use sqlglot_rust::builder::literal;
144///
145/// let num = literal(42);
146/// ```
147#[must_use]
148pub fn literal<T: ToString>(value: T) -> Expr {
149    Expr::Number(value.to_string())
150}
151
152/// Create a string literal expression.
153///
154/// # Examples
155///
156/// ```rust
157/// use sqlglot_rust::builder::string_literal;
158///
159/// let s = string_literal("hello");
160/// ```
161#[must_use]
162pub fn string_literal(value: &str) -> Expr {
163    Expr::StringLiteral(value.to_string())
164}
165
166/// Create a boolean literal expression.
167///
168/// # Examples
169///
170/// ```rust
171/// use sqlglot_rust::builder::boolean;
172///
173/// let t = boolean(true);
174/// let f = boolean(false);
175/// ```
176#[must_use]
177pub fn boolean(value: bool) -> Expr {
178    Expr::Boolean(value)
179}
180
181/// Create a NULL literal expression.
182///
183/// # Examples
184///
185/// ```rust
186/// use sqlglot_rust::builder::null;
187///
188/// let n = null();
189/// ```
190#[must_use]
191pub fn null() -> Expr {
192    Expr::Null
193}
194
195/// Create a CAST expression.
196///
197/// # Arguments
198/// * `expr` - Expression to cast
199/// * `data_type` - Target data type
200///
201/// # Examples
202///
203/// ```rust
204/// use sqlglot_rust::builder::{cast, column};
205/// use sqlglot_rust::ast::DataType;
206///
207/// let casted = cast(column("id", None), DataType::BigInt);
208/// ```
209#[must_use]
210pub fn cast(expr: Expr, data_type: DataType) -> Expr {
211    Expr::Cast {
212        expr: Box::new(expr),
213        data_type,
214    }
215}
216
217/// Combine multiple conditions with AND.
218///
219/// # Arguments
220/// * `conditions` - Iterator of expressions to combine
221///
222/// # Examples
223///
224/// ```rust
225/// use sqlglot_rust::builder::{and_all, column};
226/// use sqlglot_rust::ast::{Expr, BinaryOperator};
227///
228/// let cond1 = Expr::BinaryOp {
229///     left: Box::new(column("x", None)),
230///     op: BinaryOperator::Gt,
231///     right: Box::new(Expr::Number("1".to_string())),
232/// };
233/// let cond2 = Expr::BinaryOp {
234///     left: Box::new(column("y", None)),
235///     op: BinaryOperator::Lt,
236///     right: Box::new(Expr::Number("10".to_string())),
237/// };
238///
239/// let combined = and_all(vec![cond1, cond2]);
240/// ```
241#[must_use]
242pub fn and_all<I>(conditions: I) -> Option<Expr>
243where
244    I: IntoIterator<Item = Expr>,
245{
246    let mut iter = conditions.into_iter();
247    let first = iter.next()?;
248    Some(iter.fold(first, |acc, cond| Expr::BinaryOp {
249        left: Box::new(acc),
250        op: BinaryOperator::And,
251        right: Box::new(cond),
252    }))
253}
254
255/// Combine multiple conditions with OR.
256///
257/// # Arguments
258/// * `conditions` - Iterator of expressions to combine
259///
260/// # Examples
261///
262/// ```rust
263/// use sqlglot_rust::builder::{or_all, column};
264/// use sqlglot_rust::ast::{Expr, BinaryOperator};
265///
266/// let cond1 = Expr::BinaryOp {
267///     left: Box::new(column("status", None)),
268///     op: BinaryOperator::Eq,
269///     right: Box::new(Expr::StringLiteral("active".to_string())),
270/// };
271/// let cond2 = Expr::BinaryOp {
272///     left: Box::new(column("status", None)),
273///     op: BinaryOperator::Eq,
274///     right: Box::new(Expr::StringLiteral("pending".to_string())),
275/// };
276///
277/// let combined = or_all(vec![cond1, cond2]);
278/// ```
279#[must_use]
280pub fn or_all<I>(conditions: I) -> Option<Expr>
281where
282    I: IntoIterator<Item = Expr>,
283{
284    let mut iter = conditions.into_iter();
285    let first = iter.next()?;
286    Some(iter.fold(first, |acc, cond| Expr::BinaryOp {
287        left: Box::new(acc),
288        op: BinaryOperator::Or,
289        right: Box::new(cond),
290    }))
291}
292
293/// Negate an expression with NOT.
294///
295/// # Examples
296///
297/// ```rust
298/// use sqlglot_rust::builder::{not, column};
299///
300/// let negated = not(column("active", None));
301/// ```
302#[must_use]
303pub fn not(expr: Expr) -> Expr {
304    Expr::UnaryOp {
305        op: crate::ast::UnaryOperator::Not,
306        expr: Box::new(expr),
307    }
308}
309
310/// Create a function call expression.
311///
312/// # Arguments
313/// * `name` - Function name
314/// * `args` - Function arguments
315///
316/// # Examples
317///
318/// ```rust
319/// use sqlglot_rust::builder::{func, column};
320///
321/// let count = func("COUNT", vec![column("id", None)]);
322/// let coalesce = func("COALESCE", vec![column("name", None), sqlglot_rust::builder::string_literal("N/A")]);
323/// ```
324#[must_use]
325pub fn func(name: &str, args: Vec<Expr>) -> Expr {
326    Expr::Function {
327        name: name.to_string(),
328        args,
329        distinct: false,
330        filter: None,
331        over: None,
332        order_by: vec![],
333        within_group: false,
334    }
335}
336
337/// Create a function call with DISTINCT.
338///
339/// # Examples
340///
341/// ```rust
342/// use sqlglot_rust::builder::{func_distinct, column};
343///
344/// let count_distinct = func_distinct("COUNT", vec![column("user_id", None)]);
345/// ```
346#[must_use]
347pub fn func_distinct(name: &str, args: Vec<Expr>) -> Expr {
348    Expr::Function {
349        name: name.to_string(),
350        args,
351        distinct: true,
352        filter: None,
353        over: None,
354        order_by: vec![],
355        within_group: false,
356    }
357}
358
359/// Create a wildcard (*) expression.
360///
361/// # Examples
362///
363/// ```rust
364/// use sqlglot_rust::builder::star;
365///
366/// let all = star();
367/// ```
368#[must_use]
369pub fn star() -> Expr {
370    Expr::Star
371}
372
373/// Create a qualified wildcard (table.*) expression.
374///
375/// # Examples
376///
377/// ```rust
378/// use sqlglot_rust::builder::qualified_star;
379///
380/// let all_users = qualified_star("users");
381/// ```
382#[must_use]
383pub fn qualified_star(table: &str) -> Expr {
384    Expr::QualifiedWildcard {
385        table: table.to_string(),
386    }
387}
388
389/// Create a subquery expression.
390///
391/// # Examples
392///
393/// ```rust
394/// use sqlglot_rust::builder::{subquery, select};
395///
396/// let inner = select(&["id"]).from("users").build();
397/// let sub = subquery(inner);
398/// ```
399#[must_use]
400pub fn subquery(statement: Statement) -> Expr {
401    Expr::Subquery(Box::new(statement))
402}
403
404/// Create an EXISTS expression.
405///
406/// # Examples
407///
408/// ```rust
409/// use sqlglot_rust::builder::{exists, select};
410///
411/// let inner = select(&["1"]).from("users").where_clause("id = 1").build();
412/// let check = exists(inner, false);
413/// ```
414#[must_use]
415pub fn exists(statement: Statement, negated: bool) -> Expr {
416    Expr::Exists {
417        subquery: Box::new(statement),
418        negated,
419    }
420}
421
422/// Create an aliased expression.
423///
424/// # Examples
425///
426/// ```rust
427/// use sqlglot_rust::builder::{alias, column};
428///
429/// let aliased = alias(column("first_name", None), "name");
430/// ```
431#[must_use]
432pub fn alias(expr: Expr, name: &str) -> Expr {
433    Expr::Alias {
434        expr: Box::new(expr),
435        name: name.to_string(),
436    }
437}
438
439// ═══════════════════════════════════════════════════════════════════════
440// Comparison helpers
441// ═══════════════════════════════════════════════════════════════════════
442
443/// Create an equality comparison (=).
444#[must_use]
445pub fn eq(left: Expr, right: Expr) -> Expr {
446    Expr::BinaryOp {
447        left: Box::new(left),
448        op: BinaryOperator::Eq,
449        right: Box::new(right),
450    }
451}
452
453/// Create an inequality comparison (<>).
454#[must_use]
455pub fn neq(left: Expr, right: Expr) -> Expr {
456    Expr::BinaryOp {
457        left: Box::new(left),
458        op: BinaryOperator::Neq,
459        right: Box::new(right),
460    }
461}
462
463/// Create a less-than comparison (<).
464#[must_use]
465pub fn lt(left: Expr, right: Expr) -> Expr {
466    Expr::BinaryOp {
467        left: Box::new(left),
468        op: BinaryOperator::Lt,
469        right: Box::new(right),
470    }
471}
472
473/// Create a less-than-or-equal comparison (<=).
474#[must_use]
475pub fn lte(left: Expr, right: Expr) -> Expr {
476    Expr::BinaryOp {
477        left: Box::new(left),
478        op: BinaryOperator::LtEq,
479        right: Box::new(right),
480    }
481}
482
483/// Create a greater-than comparison (>).
484#[must_use]
485pub fn gt(left: Expr, right: Expr) -> Expr {
486    Expr::BinaryOp {
487        left: Box::new(left),
488        op: BinaryOperator::Gt,
489        right: Box::new(right),
490    }
491}
492
493/// Create a greater-than-or-equal comparison (>=).
494#[must_use]
495pub fn gte(left: Expr, right: Expr) -> Expr {
496    Expr::BinaryOp {
497        left: Box::new(left),
498        op: BinaryOperator::GtEq,
499        right: Box::new(right),
500    }
501}
502
503/// Create an IS NULL check.
504#[must_use]
505pub fn is_null(expr: Expr) -> Expr {
506    Expr::IsNull {
507        expr: Box::new(expr),
508        negated: false,
509    }
510}
511
512/// Create an IS NOT NULL check.
513#[must_use]
514pub fn is_not_null(expr: Expr) -> Expr {
515    Expr::IsNull {
516        expr: Box::new(expr),
517        negated: true,
518    }
519}
520
521/// Create a BETWEEN expression.
522#[must_use]
523pub fn between(expr: Expr, low: Expr, high: Expr) -> Expr {
524    Expr::Between {
525        expr: Box::new(expr),
526        low: Box::new(low),
527        high: Box::new(high),
528        negated: false,
529    }
530}
531
532/// Create an IN list expression.
533#[must_use]
534pub fn in_list(expr: Expr, list: Vec<Expr>) -> Expr {
535    Expr::InList {
536        expr: Box::new(expr),
537        list,
538        negated: false,
539    }
540}
541
542/// Create a NOT IN list expression.
543#[must_use]
544pub fn not_in_list(expr: Expr, list: Vec<Expr>) -> Expr {
545    Expr::InList {
546        expr: Box::new(expr),
547        list,
548        negated: true,
549    }
550}
551
552/// Create an IN subquery expression.
553#[must_use]
554pub fn in_subquery(expr: Expr, query: Statement) -> Expr {
555    Expr::InSubquery {
556        expr: Box::new(expr),
557        subquery: Box::new(query),
558        negated: false,
559    }
560}
561
562/// Create a LIKE expression.
563#[must_use]
564pub fn like(expr: Expr, pattern: Expr) -> Expr {
565    Expr::Like {
566        expr: Box::new(expr),
567        pattern: Box::new(pattern),
568        negated: false,
569        escape: None,
570    }
571}
572
573// ═══════════════════════════════════════════════════════════════════════
574// Arithmetic helpers
575// ═══════════════════════════════════════════════════════════════════════
576
577/// Create an addition expression (+).
578#[must_use]
579pub fn add(left: Expr, right: Expr) -> Expr {
580    Expr::BinaryOp {
581        left: Box::new(left),
582        op: BinaryOperator::Plus,
583        right: Box::new(right),
584    }
585}
586
587/// Create a subtraction expression (-).
588#[must_use]
589pub fn sub(left: Expr, right: Expr) -> Expr {
590    Expr::BinaryOp {
591        left: Box::new(left),
592        op: BinaryOperator::Minus,
593        right: Box::new(right),
594    }
595}
596
597/// Create a multiplication expression (*).
598#[must_use]
599pub fn mul(left: Expr, right: Expr) -> Expr {
600    Expr::BinaryOp {
601        left: Box::new(left),
602        op: BinaryOperator::Multiply,
603        right: Box::new(right),
604    }
605}
606
607/// Create a division expression (/).
608#[must_use]
609pub fn div(left: Expr, right: Expr) -> Expr {
610    Expr::BinaryOp {
611        left: Box::new(left),
612        op: BinaryOperator::Divide,
613        right: Box::new(right),
614    }
615}
616
617// ═══════════════════════════════════════════════════════════════════════
618// Parse helpers
619// ═══════════════════════════════════════════════════════════════════════
620
621/// Parse an expression string into an Expr.
622///
623/// Uses ANSI SQL dialect by default. Returns None if parsing fails.
624///
625/// # Examples
626///
627/// ```rust
628/// use sqlglot_rust::builder::parse_expr;
629///
630/// let expr = parse_expr("x + 1").unwrap();
631/// let cond = parse_expr("a = 1 AND b = 2").unwrap();
632/// ```
633#[must_use]
634pub fn parse_expr(sql: &str) -> Option<Expr> {
635    parse_expr_dialect(sql, Dialect::Ansi)
636}
637
638/// Parse an expression string into an Expr with a specific dialect.
639#[must_use]
640pub fn parse_expr_dialect(sql: &str, dialect: Dialect) -> Option<Expr> {
641    // Parse as a SELECT to extract the expression
642    let query = format!("SELECT {sql}");
643    match parse(&query, dialect) {
644        Ok(Statement::Select(select)) => {
645            if let Some(SelectItem::Expr { expr, .. }) = select.columns.first() {
646                Some(expr.clone())
647            } else {
648                None
649            }
650        }
651        _ => None,
652    }
653}
654
655/// Parse a condition string for use in WHERE clauses.
656///
657/// # Examples
658///
659/// ```rust
660/// use sqlglot_rust::builder::parse_condition;
661///
662/// let cond = parse_condition("x > 1 AND y < 10").unwrap();
663/// ```
664#[must_use]
665pub fn parse_condition(sql: &str) -> Option<Expr> {
666    parse_condition_dialect(sql, Dialect::Ansi)
667}
668
669/// Parse a condition string with a specific dialect.
670#[must_use]
671pub fn parse_condition_dialect(sql: &str, dialect: Dialect) -> Option<Expr> {
672    // Parse as a SELECT with WHERE to extract the condition
673    let query = format!("SELECT 1 WHERE {sql}");
674    match parse(&query, dialect) {
675        Ok(Statement::Select(select)) => select.where_clause,
676        _ => None,
677    }
678}
679
680// ═══════════════════════════════════════════════════════════════════════
681// Condition Builder
682// ═══════════════════════════════════════════════════════════════════════
683
684/// Builder for combining conditions with AND/OR/NOT.
685///
686/// # Examples
687///
688/// ```rust
689/// use sqlglot_rust::builder::condition;
690///
691/// let cond = condition("x = 1")
692///     .and("y = 2")
693///     .or("z = 3")
694///     .build();
695/// ```
696#[derive(Debug, Clone)]
697pub struct ConditionBuilder {
698    expr: Option<Expr>,
699    dialect: Dialect,
700}
701
702impl ConditionBuilder {
703    /// Create a new condition builder from a string.
704    #[must_use]
705    pub fn new(condition: &str) -> Self {
706        Self::new_with_dialect(condition, Dialect::Ansi)
707    }
708
709    /// Create a new condition builder with a specific dialect.
710    #[must_use]
711    pub fn new_with_dialect(condition: &str, dialect: Dialect) -> Self {
712        Self {
713            expr: parse_condition_dialect(condition, dialect),
714            dialect,
715        }
716    }
717
718    /// Create a new condition builder from an expression.
719    #[must_use]
720    pub fn from_expr(expr: Expr) -> Self {
721        Self {
722            expr: Some(expr),
723            dialect: Dialect::Ansi,
724        }
725    }
726
727    /// Add an AND condition.
728    #[must_use]
729    pub fn and(self, condition: &str) -> Self {
730        let dialect = self.dialect;
731        self.and_expr(parse_condition_dialect(condition, dialect))
732    }
733
734    /// Add an AND condition from an expression.
735    #[must_use]
736    pub fn and_expr(self, other: Option<Expr>) -> Self {
737        let expr = match (self.expr, other) {
738            (Some(left), Some(right)) => Some(Expr::BinaryOp {
739                left: Box::new(left),
740                op: BinaryOperator::And,
741                right: Box::new(right),
742            }),
743            (Some(e), None) | (None, Some(e)) => Some(e),
744            (None, None) => None,
745        };
746        Self {
747            expr,
748            dialect: self.dialect,
749        }
750    }
751
752    /// Add an OR condition.
753    #[must_use]
754    pub fn or(self, condition: &str) -> Self {
755        let dialect = self.dialect;
756        self.or_expr(parse_condition_dialect(condition, dialect))
757    }
758
759    /// Add an OR condition from an expression.
760    #[must_use]
761    pub fn or_expr(self, other: Option<Expr>) -> Self {
762        let expr = match (self.expr, other) {
763            (Some(left), Some(right)) => Some(Expr::BinaryOp {
764                left: Box::new(left),
765                op: BinaryOperator::Or,
766                right: Box::new(right),
767            }),
768            (Some(e), None) | (None, Some(e)) => Some(e),
769            (None, None) => None,
770        };
771        Self {
772            expr,
773            dialect: self.dialect,
774        }
775    }
776
777    /// Negate the current condition with NOT.
778    #[must_use]
779    pub fn not(self) -> Self {
780        let expr = self.expr.map(|e| Expr::UnaryOp {
781            op: crate::ast::UnaryOperator::Not,
782            expr: Box::new(e),
783        });
784        Self {
785            expr,
786            dialect: self.dialect,
787        }
788    }
789
790    /// Build the final expression.
791    #[must_use]
792    pub fn build(self) -> Option<Expr> {
793        self.expr
794    }
795}
796
797/// Create a new condition builder.
798///
799/// # Examples
800///
801/// ```rust
802/// use sqlglot_rust::builder::condition;
803///
804/// let cond = condition("x = 1").and("y = 2").build();
805/// ```
806#[must_use]
807pub fn condition(cond: &str) -> ConditionBuilder {
808    ConditionBuilder::new(cond)
809}
810
811/// Create a condition builder with a specific dialect.
812#[must_use]
813pub fn condition_dialect(cond: &str, dialect: Dialect) -> ConditionBuilder {
814    ConditionBuilder::new_with_dialect(cond, dialect)
815}
816
817// ═══════════════════════════════════════════════════════════════════════
818// SELECT Builder
819// ═══════════════════════════════════════════════════════════════════════
820
821/// Fluent builder for SELECT statements.
822///
823/// # Examples
824///
825/// ```rust
826/// use sqlglot_rust::builder::select;
827///
828/// let query = select(&["a", "b"])
829///     .from("users")
830///     .where_clause("active = true")
831///     .order_by(&["created_at DESC"])
832///     .limit(10)
833///     .build();
834/// ```
835#[derive(Debug, Clone)]
836pub struct SelectBuilder {
837    statement: SelectStatement,
838    dialect: Dialect,
839}
840
841impl Default for SelectBuilder {
842    fn default() -> Self {
843        Self::new()
844    }
845}
846
847impl SelectBuilder {
848    /// Create a new empty SELECT builder.
849    #[must_use]
850    pub fn new() -> Self {
851        Self {
852            statement: SelectStatement {
853                comments: Vec::new(),
854                ctes: Vec::new(),
855                distinct: false,
856                top: None,
857                columns: Vec::new(),
858                from: None,
859                joins: Vec::new(),
860                where_clause: None,
861                group_by: Vec::new(),
862                having: None,
863                order_by: Vec::new(),
864                limit: None,
865                offset: None,
866                fetch_first: None,
867                qualify: None,
868                window_definitions: Vec::new(),
869            },
870            dialect: Dialect::Ansi,
871        }
872    }
873
874    /// Set the dialect for parsing string inputs.
875    #[must_use]
876    pub fn dialect(mut self, dialect: Dialect) -> Self {
877        self.dialect = dialect;
878        self
879    }
880
881    /// Add columns to the SELECT list from strings.
882    ///
883    /// Each string is parsed as an expression.
884    #[must_use]
885    pub fn columns(mut self, cols: &[&str]) -> Self {
886        for col in cols {
887            if let Some(expr) = parse_expr_dialect(col, self.dialect) {
888                self.statement.columns.push(SelectItem::Expr {
889                    expr,
890                    alias: None,
891                    alias_quote_style: QuoteStyle::None,
892                });
893            }
894        }
895        self
896    }
897
898    /// Add a single column expression.
899    #[must_use]
900    pub fn column_expr(mut self, expr: Expr, alias: Option<&str>) -> Self {
901        self.statement.columns.push(SelectItem::Expr {
902            expr,
903            alias: alias.map(String::from),
904            alias_quote_style: QuoteStyle::None,
905        });
906        self
907    }
908
909    /// Add a wildcard (*) to the SELECT list.
910    #[must_use]
911    pub fn all(mut self) -> Self {
912        self.statement.columns.push(SelectItem::Wildcard);
913        self
914    }
915
916    /// Add a qualified wildcard (table.*) to the SELECT list.
917    #[must_use]
918    pub fn all_from(mut self, table: &str) -> Self {
919        self.statement.columns.push(SelectItem::QualifiedWildcard {
920            table: table.to_string(),
921        });
922        self
923    }
924
925    /// Set distinct mode.
926    #[must_use]
927    pub fn distinct(mut self) -> Self {
928        self.statement.distinct = true;
929        self
930    }
931
932    /// Set the FROM clause to a table name.
933    #[must_use]
934    pub fn from(mut self, table_name: &str) -> Self {
935        self.statement.from = Some(FromClause {
936            source: TableSource::Table(table(table_name, None)),
937        });
938        self
939    }
940
941    /// Set the FROM clause to a table reference.
942    #[must_use]
943    pub fn from_table(mut self, table_ref: TableRef) -> Self {
944        self.statement.from = Some(FromClause {
945            source: TableSource::Table(table_ref),
946        });
947        self
948    }
949
950    /// Set the FROM clause to a subquery.
951    #[must_use]
952    pub fn from_subquery(mut self, query: Statement, alias: &str) -> Self {
953        self.statement.from = Some(FromClause {
954            source: TableSource::Subquery {
955                query: Box::new(query),
956                alias: Some(alias.to_string()),
957                alias_quote_style: QuoteStyle::None,
958            },
959        });
960        self
961    }
962
963    /// Add a JOIN clause.
964    #[must_use]
965    pub fn join(self, table_name: &str, on: &str) -> Self {
966        self.join_type(table_name, on, JoinType::Inner)
967    }
968
969    /// Add a LEFT JOIN clause.
970    #[must_use]
971    pub fn left_join(self, table_name: &str, on: &str) -> Self {
972        self.join_type(table_name, on, JoinType::Left)
973    }
974
975    /// Add a RIGHT JOIN clause.
976    #[must_use]
977    pub fn right_join(self, table_name: &str, on: &str) -> Self {
978        self.join_type(table_name, on, JoinType::Right)
979    }
980
981    /// Add a FULL JOIN clause.
982    #[must_use]
983    pub fn full_join(self, table_name: &str, on: &str) -> Self {
984        self.join_type(table_name, on, JoinType::Full)
985    }
986
987    /// Add a CROSS JOIN clause.
988    #[must_use]
989    pub fn cross_join(mut self, table_name: &str) -> Self {
990        self.statement.joins.push(JoinClause {
991            join_type: JoinType::Cross,
992            table: TableSource::Table(table(table_name, None)),
993            on: None,
994            using: Vec::new(),
995        });
996        self
997    }
998
999    /// Add a JOIN with a specific type.
1000    #[must_use]
1001    fn join_type(mut self, table_name: &str, on: &str, join_type: JoinType) -> Self {
1002        let on_expr = parse_condition_dialect(on, self.dialect);
1003        self.statement.joins.push(JoinClause {
1004            join_type,
1005            table: TableSource::Table(table(table_name, None)),
1006            on: on_expr,
1007            using: Vec::new(),
1008        });
1009        self
1010    }
1011
1012    /// Add a JOIN with USING clause.
1013    #[must_use]
1014    pub fn join_using(mut self, table_name: &str, columns: &[&str], join_type: JoinType) -> Self {
1015        self.statement.joins.push(JoinClause {
1016            join_type,
1017            table: TableSource::Table(table(table_name, None)),
1018            on: None,
1019            using: columns.iter().map(|s| s.to_string()).collect(),
1020        });
1021        self
1022    }
1023
1024    /// Add a JOIN with a subquery.
1025    #[must_use]
1026    pub fn join_subquery(
1027        mut self,
1028        query: Statement,
1029        alias: &str,
1030        on: &str,
1031        join_type: JoinType,
1032    ) -> Self {
1033        let on_expr = parse_condition_dialect(on, self.dialect);
1034        self.statement.joins.push(JoinClause {
1035            join_type,
1036            table: TableSource::Subquery {
1037                query: Box::new(query),
1038                alias: Some(alias.to_string()),
1039                alias_quote_style: QuoteStyle::None,
1040            },
1041            on: on_expr,
1042            using: Vec::new(),
1043        });
1044        self
1045    }
1046
1047    /// Set the WHERE clause from a string.
1048    #[must_use]
1049    pub fn where_clause(mut self, condition: &str) -> Self {
1050        self.statement.where_clause = parse_condition_dialect(condition, self.dialect);
1051        self
1052    }
1053
1054    /// Set the WHERE clause from an expression.
1055    #[must_use]
1056    pub fn where_expr(mut self, expr: Expr) -> Self {
1057        self.statement.where_clause = Some(expr);
1058        self
1059    }
1060
1061    /// Add to the WHERE clause with AND.
1062    #[must_use]
1063    pub fn and_where(mut self, condition: &str) -> Self {
1064        let new_cond = parse_condition_dialect(condition, self.dialect);
1065        self.statement.where_clause = match (self.statement.where_clause, new_cond) {
1066            (Some(existing), Some(new)) => Some(Expr::BinaryOp {
1067                left: Box::new(existing),
1068                op: BinaryOperator::And,
1069                right: Box::new(new),
1070            }),
1071            (Some(e), None) | (None, Some(e)) => Some(e),
1072            (None, None) => None,
1073        };
1074        self
1075    }
1076
1077    /// Add to the WHERE clause with OR.
1078    #[must_use]
1079    pub fn or_where(mut self, condition: &str) -> Self {
1080        let new_cond = parse_condition_dialect(condition, self.dialect);
1081        self.statement.where_clause = match (self.statement.where_clause, new_cond) {
1082            (Some(existing), Some(new)) => Some(Expr::BinaryOp {
1083                left: Box::new(existing),
1084                op: BinaryOperator::Or,
1085                right: Box::new(new),
1086            }),
1087            (Some(e), None) | (None, Some(e)) => Some(e),
1088            (None, None) => None,
1089        };
1090        self
1091    }
1092
1093    /// Set the GROUP BY clause.
1094    #[must_use]
1095    pub fn group_by(mut self, exprs: &[&str]) -> Self {
1096        self.statement.group_by = exprs
1097            .iter()
1098            .filter_map(|e| parse_expr_dialect(e, self.dialect))
1099            .collect();
1100        self
1101    }
1102
1103    /// Add a GROUP BY expression.
1104    #[must_use]
1105    pub fn add_group_by(mut self, expr: &str) -> Self {
1106        if let Some(e) = parse_expr_dialect(expr, self.dialect) {
1107            self.statement.group_by.push(e);
1108        }
1109        self
1110    }
1111
1112    /// Set the HAVING clause.
1113    #[must_use]
1114    pub fn having(mut self, condition: &str) -> Self {
1115        self.statement.having = parse_condition_dialect(condition, self.dialect);
1116        self
1117    }
1118
1119    /// Set the ORDER BY clause.
1120    #[must_use]
1121    pub fn order_by(mut self, exprs: &[&str]) -> Self {
1122        self.statement.order_by = exprs
1123            .iter()
1124            .filter_map(|e| parse_order_by_item(e, self.dialect))
1125            .collect();
1126        self
1127    }
1128
1129    /// Add an ORDER BY item.
1130    #[must_use]
1131    pub fn add_order_by(mut self, expr: &str) -> Self {
1132        if let Some(item) = parse_order_by_item(expr, self.dialect) {
1133            self.statement.order_by.push(item);
1134        }
1135        self
1136    }
1137
1138    /// Add an ORDER BY item with explicit direction.
1139    #[must_use]
1140    pub fn add_order_by_expr(
1141        mut self,
1142        expr: Expr,
1143        ascending: bool,
1144        nulls_first: Option<bool>,
1145    ) -> Self {
1146        self.statement.order_by.push(OrderByItem {
1147            expr,
1148            ascending,
1149            nulls_first,
1150        });
1151        self
1152    }
1153
1154    /// Set the LIMIT clause.
1155    #[must_use]
1156    pub fn limit(mut self, n: i64) -> Self {
1157        self.statement.limit = Some(Expr::Number(n.to_string()));
1158        self
1159    }
1160
1161    /// Set the LIMIT clause from an expression.
1162    #[must_use]
1163    pub fn limit_expr(mut self, expr: Expr) -> Self {
1164        self.statement.limit = Some(expr);
1165        self
1166    }
1167
1168    /// Set the OFFSET clause.
1169    #[must_use]
1170    pub fn offset(mut self, n: i64) -> Self {
1171        self.statement.offset = Some(Expr::Number(n.to_string()));
1172        self
1173    }
1174
1175    /// Set the OFFSET clause from an expression.
1176    #[must_use]
1177    pub fn offset_expr(mut self, expr: Expr) -> Self {
1178        self.statement.offset = Some(expr);
1179        self
1180    }
1181
1182    /// Set TOP N (T-SQL style).
1183    #[must_use]
1184    pub fn top(mut self, n: i64) -> Self {
1185        self.statement.top = Some(Box::new(Expr::Number(n.to_string())));
1186        self
1187    }
1188
1189    /// Set the QUALIFY clause (BigQuery, Snowflake).
1190    #[must_use]
1191    pub fn qualify(mut self, condition: &str) -> Self {
1192        self.statement.qualify = parse_condition_dialect(condition, self.dialect);
1193        self
1194    }
1195
1196    /// Build the final SELECT statement.
1197    #[must_use]
1198    pub fn build(self) -> Statement {
1199        Statement::Select(self.statement)
1200    }
1201
1202    /// Build and return the inner SelectStatement.
1203    #[must_use]
1204    pub fn build_select(self) -> SelectStatement {
1205        self.statement
1206    }
1207}
1208
1209/// Create a new SELECT builder with columns.
1210///
1211/// # Examples
1212///
1213/// ```rust
1214/// use sqlglot_rust::builder::select;
1215///
1216/// let query = select(&["a", "b", "c"]).from("table_name").build();
1217/// ```
1218#[must_use]
1219pub fn select(columns: &[&str]) -> SelectBuilder {
1220    SelectBuilder::new().columns(columns)
1221}
1222
1223/// Create a SELECT * query.
1224///
1225/// # Examples
1226///
1227/// ```rust
1228/// use sqlglot_rust::builder::select_all;
1229///
1230/// let query = select_all().from("users").build();
1231/// ```
1232#[must_use]
1233pub fn select_all() -> SelectBuilder {
1234    SelectBuilder::new().all()
1235}
1236
1237/// Create a SELECT DISTINCT builder.
1238///
1239/// # Examples
1240///
1241/// ```rust
1242/// use sqlglot_rust::builder::select_distinct;
1243///
1244/// let query = select_distinct(&["category"]).from("products").build();
1245/// ```
1246#[must_use]
1247pub fn select_distinct(columns: &[&str]) -> SelectBuilder {
1248    SelectBuilder::new().distinct().columns(columns)
1249}
1250
1251// ═══════════════════════════════════════════════════════════════════════
1252// Statement Mutation Methods
1253// ═══════════════════════════════════════════════════════════════════════
1254
1255impl SelectStatement {
1256    /// Add a column to the SELECT list.
1257    ///
1258    /// # Examples
1259    ///
1260    /// ```rust
1261    /// use sqlglot_rust::builder::select;
1262    ///
1263    /// let mut stmt = select(&["a"]).from("t").build_select();
1264    /// stmt.add_select("b");
1265    /// ```
1266    pub fn add_select(&mut self, expr_str: &str) {
1267        self.add_select_dialect(expr_str, Dialect::Ansi);
1268    }
1269
1270    /// Add a column with dialect-specific parsing.
1271    pub fn add_select_dialect(&mut self, expr_str: &str, dialect: Dialect) {
1272        if let Some(expr) = parse_expr_dialect(expr_str, dialect) {
1273            self.columns.push(SelectItem::Expr {
1274                expr,
1275                alias: None,
1276                alias_quote_style: QuoteStyle::None,
1277            });
1278        }
1279    }
1280
1281    /// Add an expression to the SELECT list.
1282    pub fn add_select_expr(&mut self, expr: Expr, alias: Option<&str>) {
1283        self.columns.push(SelectItem::Expr {
1284            expr,
1285            alias: alias.map(String::from),
1286            alias_quote_style: QuoteStyle::None,
1287        });
1288    }
1289
1290    /// Add a condition to the WHERE clause (AND).
1291    ///
1292    /// # Examples
1293    ///
1294    /// ```rust
1295    /// use sqlglot_rust::builder::select;
1296    ///
1297    /// let mut stmt = select(&["a"]).from("t").build_select();
1298    /// stmt.add_where("x > 1");
1299    /// stmt.add_where("y < 10");
1300    /// ```
1301    pub fn add_where(&mut self, condition: &str) {
1302        self.add_where_dialect(condition, Dialect::Ansi);
1303    }
1304
1305    /// Add a WHERE condition with dialect-specific parsing.
1306    pub fn add_where_dialect(&mut self, condition: &str, dialect: Dialect) {
1307        let new_cond = parse_condition_dialect(condition, dialect);
1308        self.where_clause = match (self.where_clause.take(), new_cond) {
1309            (Some(existing), Some(new)) => Some(Expr::BinaryOp {
1310                left: Box::new(existing),
1311                op: BinaryOperator::And,
1312                right: Box::new(new),
1313            }),
1314            (Some(e), None) | (None, Some(e)) => Some(e),
1315            (None, None) => None,
1316        };
1317    }
1318
1319    /// Add an expression to the WHERE clause (AND).
1320    pub fn add_where_expr(&mut self, expr: Expr) {
1321        self.where_clause = match self.where_clause.take() {
1322            Some(existing) => Some(Expr::BinaryOp {
1323                left: Box::new(existing),
1324                op: BinaryOperator::And,
1325                right: Box::new(expr),
1326            }),
1327            None => Some(expr),
1328        };
1329    }
1330
1331    /// Add a JOIN clause.
1332    ///
1333    /// # Examples
1334    ///
1335    /// ```rust
1336    /// use sqlglot_rust::builder::select;
1337    /// use sqlglot_rust::ast::JoinType;
1338    ///
1339    /// let mut stmt = select(&["*"]).from("users").build_select();
1340    /// stmt.add_join("orders", "users.id = orders.user_id", JoinType::Left);
1341    /// ```
1342    pub fn add_join(&mut self, table_name: &str, on: &str, join_type: JoinType) {
1343        self.add_join_dialect(table_name, on, join_type, Dialect::Ansi);
1344    }
1345
1346    /// Add a JOIN with dialect-specific parsing.
1347    pub fn add_join_dialect(
1348        &mut self,
1349        table_name: &str,
1350        on: &str,
1351        join_type: JoinType,
1352        dialect: Dialect,
1353    ) {
1354        let on_expr = parse_condition_dialect(on, dialect);
1355        self.joins.push(JoinClause {
1356            join_type,
1357            table: TableSource::Table(table(table_name, None)),
1358            on: on_expr,
1359            using: Vec::new(),
1360        });
1361    }
1362
1363    /// Add a JOIN with a subquery.
1364    pub fn add_join_subquery(
1365        &mut self,
1366        query: Statement,
1367        alias: &str,
1368        on: &str,
1369        join_type: JoinType,
1370    ) {
1371        self.add_join_subquery_dialect(query, alias, on, join_type, Dialect::Ansi);
1372    }
1373
1374    /// Add a JOIN with a subquery and dialect-specific parsing.
1375    pub fn add_join_subquery_dialect(
1376        &mut self,
1377        query: Statement,
1378        alias: &str,
1379        on: &str,
1380        join_type: JoinType,
1381        dialect: Dialect,
1382    ) {
1383        let on_expr = parse_condition_dialect(on, dialect);
1384        self.joins.push(JoinClause {
1385            join_type,
1386            table: TableSource::Subquery {
1387                query: Box::new(query),
1388                alias: Some(alias.to_string()),
1389                alias_quote_style: QuoteStyle::None,
1390            },
1391            on: on_expr,
1392            using: Vec::new(),
1393        });
1394    }
1395
1396    /// Wrap this SELECT as a subquery with an alias.
1397    ///
1398    /// # Examples
1399    ///
1400    /// ```rust
1401    /// use sqlglot_rust::builder::select;
1402    ///
1403    /// let inner = select(&["id", "name"]).from("users").build_select();
1404    /// let subq = inner.as_subquery("u");
1405    /// ```
1406    #[must_use]
1407    pub fn as_subquery(self, alias: &str) -> TableSource {
1408        TableSource::Subquery {
1409            query: Box::new(Statement::Select(self)),
1410            alias: Some(alias.to_string()),
1411            alias_quote_style: QuoteStyle::None,
1412        }
1413    }
1414}
1415
1416// ═══════════════════════════════════════════════════════════════════════
1417// Helper functions
1418// ═══════════════════════════════════════════════════════════════════════
1419
1420/// Parse an ORDER BY item string like "col ASC" or "col DESC NULLS FIRST"
1421fn parse_order_by_item(s: &str, dialect: Dialect) -> Option<OrderByItem> {
1422    let s = s.trim();
1423    let upper = s.to_uppercase();
1424
1425    // Check for NULLS FIRST/LAST
1426    let nulls_first = if upper.contains("NULLS FIRST") {
1427        Some(true)
1428    } else if upper.contains("NULLS LAST") {
1429        Some(false)
1430    } else {
1431        None
1432    };
1433
1434    // Remove NULLS clause for parsing
1435    let s = s
1436        .replace("NULLS FIRST", "")
1437        .replace("NULLS LAST", "")
1438        .replace("nulls first", "")
1439        .replace("nulls last", "");
1440    let s = s.trim();
1441
1442    // Check for ASC/DESC
1443    let (expr_str, ascending) = if s.to_uppercase().ends_with(" DESC") {
1444        (&s[..s.len() - 5], false)
1445    } else if s.to_uppercase().ends_with(" ASC") {
1446        (&s[..s.len() - 4], true)
1447    } else {
1448        (s, true)
1449    };
1450
1451    parse_expr_dialect(expr_str.trim(), dialect).map(|expr| OrderByItem {
1452        expr,
1453        ascending,
1454        nulls_first,
1455    })
1456}
1457
1458#[cfg(test)]
1459mod tests {
1460    use super::*;
1461    use crate::generate;
1462
1463    #[test]
1464    fn test_column() {
1465        let col = column("name", None);
1466        assert!(
1467            matches!(col, Expr::Column { name, table, .. } if name == "name" && table.is_none())
1468        );
1469
1470        let qualified = column("id", Some("users"));
1471        assert!(matches!(qualified, Expr::Column { name, table, .. }
1472            if name == "id" && table == Some("users".to_string())));
1473    }
1474
1475    #[test]
1476    fn test_table() {
1477        let tbl = table("users", None);
1478        assert_eq!(tbl.name, "users");
1479        assert!(tbl.schema.is_none());
1480
1481        let qualified = table("orders", Some("public"));
1482        assert_eq!(qualified.name, "orders");
1483        assert_eq!(qualified.schema, Some("public".to_string()));
1484    }
1485
1486    #[test]
1487    fn test_literals() {
1488        assert!(matches!(literal(42), Expr::Number(n) if n == "42"));
1489        assert!(matches!(string_literal("hello"), Expr::StringLiteral(s) if s == "hello"));
1490        assert!(matches!(boolean(true), Expr::Boolean(true)));
1491        assert!(matches!(null(), Expr::Null));
1492    }
1493
1494    #[test]
1495    fn test_cast() {
1496        let col = column("id", None);
1497        let casted = cast(col, DataType::BigInt);
1498        assert!(matches!(
1499            casted,
1500            Expr::Cast {
1501                data_type: DataType::BigInt,
1502                ..
1503            }
1504        ));
1505    }
1506
1507    #[test]
1508    fn test_and_all() {
1509        let cond1 = eq(column("x", None), literal(1));
1510        let cond2 = eq(column("y", None), literal(2));
1511
1512        let combined = and_all(vec![cond1, cond2]).unwrap();
1513        assert!(matches!(
1514            combined,
1515            Expr::BinaryOp {
1516                op: BinaryOperator::And,
1517                ..
1518            }
1519        ));
1520
1521        // Empty returns None
1522        assert!(and_all(Vec::<Expr>::new()).is_none());
1523    }
1524
1525    #[test]
1526    fn test_or_all() {
1527        let cond1 = eq(column("x", None), literal(1));
1528        let cond2 = eq(column("y", None), literal(2));
1529
1530        let combined = or_all(vec![cond1, cond2]).unwrap();
1531        assert!(matches!(
1532            combined,
1533            Expr::BinaryOp {
1534                op: BinaryOperator::Or,
1535                ..
1536            }
1537        ));
1538    }
1539
1540    #[test]
1541    fn test_parse_expr() {
1542        let expr = parse_expr("x + 1").unwrap();
1543        assert!(matches!(
1544            expr,
1545            Expr::BinaryOp {
1546                op: BinaryOperator::Plus,
1547                ..
1548            }
1549        ));
1550    }
1551
1552    #[test]
1553    fn test_parse_condition() {
1554        let cond = parse_condition("x > 1 AND y < 10").unwrap();
1555        assert!(matches!(
1556            cond,
1557            Expr::BinaryOp {
1558                op: BinaryOperator::And,
1559                ..
1560            }
1561        ));
1562    }
1563
1564    #[test]
1565    fn test_condition_builder() {
1566        let cond = condition("x = 1").and("y = 2").or("z = 3").build();
1567        assert!(cond.is_some());
1568    }
1569
1570    #[test]
1571    fn test_condition_builder_not() {
1572        let cond = condition("x = 1").not().build().unwrap();
1573        assert!(matches!(
1574            cond,
1575            Expr::UnaryOp {
1576                op: crate::ast::UnaryOperator::Not,
1577                ..
1578            }
1579        ));
1580    }
1581
1582    #[test]
1583    fn test_select_builder_basic() {
1584        let query = select(&["a", "b"]).from("users").build();
1585        let sql = generate(&query, Dialect::Ansi);
1586        assert!(sql.contains("SELECT"));
1587        assert!(sql.contains("a"));
1588        assert!(sql.contains("b"));
1589        assert!(sql.contains("FROM users"));
1590    }
1591
1592    #[test]
1593    fn test_select_builder_where() {
1594        let query = select(&["*"])
1595            .from("users")
1596            .where_clause("active = true")
1597            .build();
1598        let sql = generate(&query, Dialect::Ansi);
1599        assert!(sql.contains("WHERE"));
1600    }
1601
1602    #[test]
1603    fn test_select_builder_join() {
1604        let query = select(&["u.name", "o.total"])
1605            .from("users")
1606            .join("orders", "users.id = orders.user_id")
1607            .build();
1608        let sql = generate(&query, Dialect::Ansi);
1609        assert!(sql.contains("JOIN"));
1610    }
1611
1612    #[test]
1613    fn test_select_builder_group_by() {
1614        let query = select(&["category", "COUNT(*)"])
1615            .from("products")
1616            .group_by(&["category"])
1617            .having("COUNT(*) > 5")
1618            .build();
1619        let sql = generate(&query, Dialect::Ansi);
1620        assert!(sql.contains("GROUP BY"));
1621        assert!(sql.contains("HAVING"));
1622    }
1623
1624    #[test]
1625    fn test_select_builder_order_limit() {
1626        let query = select(&["*"])
1627            .from("users")
1628            .order_by(&["created_at DESC"])
1629            .limit(10)
1630            .offset(5)
1631            .build();
1632        let sql = generate(&query, Dialect::Ansi);
1633        assert!(sql.contains("ORDER BY"));
1634        assert!(sql.contains("LIMIT 10"));
1635        assert!(sql.contains("OFFSET 5"));
1636    }
1637
1638    #[test]
1639    fn test_select_builder_distinct() {
1640        let query = select_distinct(&["category"]).from("products").build();
1641        let sql = generate(&query, Dialect::Ansi);
1642        assert!(sql.contains("SELECT DISTINCT"));
1643    }
1644
1645    #[test]
1646    fn test_select_all() {
1647        let query = select_all().from("users").build();
1648        let sql = generate(&query, Dialect::Ansi);
1649        assert!(sql.contains("SELECT *"));
1650    }
1651
1652    #[test]
1653    fn test_mutation_add_select() {
1654        let mut stmt = select(&["a"]).from("t").build_select();
1655        stmt.add_select("b");
1656        assert_eq!(stmt.columns.len(), 2);
1657    }
1658
1659    #[test]
1660    fn test_mutation_add_where() {
1661        let mut stmt = select(&["*"]).from("t").build_select();
1662        stmt.add_where("x > 1");
1663        stmt.add_where("y < 10");
1664        // Should be combined with AND
1665        assert!(stmt.where_clause.is_some());
1666    }
1667
1668    #[test]
1669    fn test_mutation_add_join() {
1670        let mut stmt = select(&["*"]).from("users").build_select();
1671        stmt.add_join("orders", "users.id = orders.user_id", JoinType::Inner);
1672        assert_eq!(stmt.joins.len(), 1);
1673    }
1674
1675    #[test]
1676    fn test_as_subquery() {
1677        let inner = select(&["id"]).from("users").build_select();
1678        let source = inner.as_subquery("u");
1679        assert!(matches!(source, TableSource::Subquery { alias: Some(a), .. } if a == "u"));
1680    }
1681
1682    #[test]
1683    fn test_comparison_helpers() {
1684        let e = eq(column("a", None), literal(1));
1685        assert!(matches!(
1686            e,
1687            Expr::BinaryOp {
1688                op: BinaryOperator::Eq,
1689                ..
1690            }
1691        ));
1692
1693        let e = neq(column("a", None), literal(1));
1694        assert!(matches!(
1695            e,
1696            Expr::BinaryOp {
1697                op: BinaryOperator::Neq,
1698                ..
1699            }
1700        ));
1701
1702        let e = lt(column("a", None), literal(1));
1703        assert!(matches!(
1704            e,
1705            Expr::BinaryOp {
1706                op: BinaryOperator::Lt,
1707                ..
1708            }
1709        ));
1710
1711        let e = gt(column("a", None), literal(1));
1712        assert!(matches!(
1713            e,
1714            Expr::BinaryOp {
1715                op: BinaryOperator::Gt,
1716                ..
1717            }
1718        ));
1719    }
1720
1721    #[test]
1722    fn test_arithmetic_helpers() {
1723        let e = add(column("a", None), literal(1));
1724        assert!(matches!(
1725            e,
1726            Expr::BinaryOp {
1727                op: BinaryOperator::Plus,
1728                ..
1729            }
1730        ));
1731
1732        let e = mul(column("a", None), literal(2));
1733        assert!(matches!(
1734            e,
1735            Expr::BinaryOp {
1736                op: BinaryOperator::Multiply,
1737                ..
1738            }
1739        ));
1740    }
1741
1742    #[test]
1743    fn test_is_null_helpers() {
1744        let e = is_null(column("a", None));
1745        assert!(matches!(e, Expr::IsNull { negated: false, .. }));
1746
1747        let e = is_not_null(column("a", None));
1748        assert!(matches!(e, Expr::IsNull { negated: true, .. }));
1749    }
1750
1751    #[test]
1752    fn test_between() {
1753        let e = between(column("x", None), literal(1), literal(10));
1754        assert!(matches!(e, Expr::Between { negated: false, .. }));
1755    }
1756
1757    #[test]
1758    fn test_in_list() {
1759        let e = in_list(
1760            column("status", None),
1761            vec![string_literal("active"), string_literal("pending")],
1762        );
1763        assert!(matches!(e, Expr::InList { negated: false, .. }));
1764    }
1765
1766    #[test]
1767    fn test_like() {
1768        let e = like(column("name", None), string_literal("%John%"));
1769        assert!(matches!(e, Expr::Like { negated: false, .. }));
1770    }
1771
1772    #[test]
1773    fn test_func() {
1774        let f = func("UPPER", vec![column("name", None)]);
1775        assert!(matches!(f, Expr::Function { name, distinct: false, .. } if name == "UPPER"));
1776    }
1777
1778    #[test]
1779    fn test_func_distinct() {
1780        let f = func_distinct("COUNT", vec![column("id", None)]);
1781        assert!(matches!(f, Expr::Function { name, distinct: true, .. } if name == "COUNT"));
1782    }
1783
1784    #[test]
1785    fn test_alias() {
1786        let e = alias(column("first_name", None), "name");
1787        assert!(matches!(e, Expr::Alias { name, .. } if name == "name"));
1788    }
1789
1790    #[test]
1791    fn test_subquery_and_exists() {
1792        let inner = select(&["1"]).from("users").where_clause("id = 1").build();
1793        let sub = subquery(inner.clone());
1794        assert!(matches!(sub, Expr::Subquery(_)));
1795
1796        let ex = exists(inner, false);
1797        assert!(matches!(ex, Expr::Exists { negated: false, .. }));
1798    }
1799
1800    #[test]
1801    fn test_star_and_qualified_star() {
1802        let s = star();
1803        assert!(matches!(s, Expr::Star));
1804
1805        let qs = qualified_star("users");
1806        assert!(matches!(qs, Expr::QualifiedWildcard { table } if table == "users"));
1807    }
1808
1809    #[test]
1810    fn test_complex_query() {
1811        // Build a more complex query
1812        let query = select(&["u.id", "u.name", "COUNT(o.id) AS order_count"])
1813            .from("users")
1814            .join("orders", "u.id = o.user_id")
1815            .where_clause("u.active = true")
1816            .and_where("o.created_at > '2024-01-01'")
1817            .group_by(&["u.id", "u.name"])
1818            .having("COUNT(o.id) > 0")
1819            .order_by(&["order_count DESC"])
1820            .limit(10)
1821            .build();
1822
1823        let sql = generate(&query, Dialect::Postgres);
1824        assert!(sql.contains("SELECT"));
1825        assert!(sql.contains("JOIN"));
1826        assert!(sql.contains("WHERE"));
1827        assert!(sql.contains("GROUP BY"));
1828        assert!(sql.contains("HAVING"));
1829        assert!(sql.contains("ORDER BY"));
1830        assert!(sql.contains("LIMIT"));
1831    }
1832
1833    #[test]
1834    fn test_subquery_in_from() {
1835        let inner = select(&["id", "name"])
1836            .from("users")
1837            .where_clause("active = true")
1838            .build();
1839        let outer = select(&["*"]).from_subquery(inner, "active_users").build();
1840
1841        let sql = generate(&outer, Dialect::Ansi);
1842        assert!(sql.contains("FROM (SELECT"));
1843        assert!(sql.contains(") AS active_users"));
1844    }
1845}