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