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
885                    .columns
886                    .push(SelectItem::Expr { expr, alias: None, alias_quote_style: QuoteStyle::None });
887            }
888        }
889        self
890    }
891
892    /// Add a single column expression.
893    #[must_use]
894    pub fn column_expr(mut self, expr: Expr, alias: Option<&str>) -> Self {
895        self.statement.columns.push(SelectItem::Expr {
896            expr,
897            alias: alias.map(String::from),
898            alias_quote_style: QuoteStyle::None,
899        });
900        self
901    }
902
903    /// Add a wildcard (*) to the SELECT list.
904    #[must_use]
905    pub fn all(mut self) -> Self {
906        self.statement.columns.push(SelectItem::Wildcard);
907        self
908    }
909
910    /// Add a qualified wildcard (table.*) to the SELECT list.
911    #[must_use]
912    pub fn all_from(mut self, table: &str) -> Self {
913        self.statement.columns.push(SelectItem::QualifiedWildcard {
914            table: table.to_string(),
915        });
916        self
917    }
918
919    /// Set distinct mode.
920    #[must_use]
921    pub fn distinct(mut self) -> Self {
922        self.statement.distinct = true;
923        self
924    }
925
926    /// Set the FROM clause to a table name.
927    #[must_use]
928    pub fn from(mut self, table_name: &str) -> Self {
929        self.statement.from = Some(FromClause {
930            source: TableSource::Table(table(table_name, None)),
931        });
932        self
933    }
934
935    /// Set the FROM clause to a table reference.
936    #[must_use]
937    pub fn from_table(mut self, table_ref: TableRef) -> Self {
938        self.statement.from = Some(FromClause {
939            source: TableSource::Table(table_ref),
940        });
941        self
942    }
943
944    /// Set the FROM clause to a subquery.
945    #[must_use]
946    pub fn from_subquery(mut self, query: Statement, alias: &str) -> Self {
947        self.statement.from = Some(FromClause {
948            source: TableSource::Subquery {
949                query: Box::new(query),
950                alias: Some(alias.to_string()),
951                alias_quote_style: QuoteStyle::None,
952            },
953        });
954        self
955    }
956
957    /// Add a JOIN clause.
958    #[must_use]
959    pub fn join(self, table_name: &str, on: &str) -> Self {
960        self.join_type(table_name, on, JoinType::Inner)
961    }
962
963    /// Add a LEFT JOIN clause.
964    #[must_use]
965    pub fn left_join(self, table_name: &str, on: &str) -> Self {
966        self.join_type(table_name, on, JoinType::Left)
967    }
968
969    /// Add a RIGHT JOIN clause.
970    #[must_use]
971    pub fn right_join(self, table_name: &str, on: &str) -> Self {
972        self.join_type(table_name, on, JoinType::Right)
973    }
974
975    /// Add a FULL JOIN clause.
976    #[must_use]
977    pub fn full_join(self, table_name: &str, on: &str) -> Self {
978        self.join_type(table_name, on, JoinType::Full)
979    }
980
981    /// Add a CROSS JOIN clause.
982    #[must_use]
983    pub fn cross_join(mut self, table_name: &str) -> Self {
984        self.statement.joins.push(JoinClause {
985            join_type: JoinType::Cross,
986            table: TableSource::Table(table(table_name, None)),
987            on: None,
988            using: Vec::new(),
989        });
990        self
991    }
992
993    /// Add a JOIN with a specific type.
994    #[must_use]
995    fn join_type(mut self, table_name: &str, on: &str, join_type: JoinType) -> Self {
996        let on_expr = parse_condition_dialect(on, self.dialect);
997        self.statement.joins.push(JoinClause {
998            join_type,
999            table: TableSource::Table(table(table_name, None)),
1000            on: on_expr,
1001            using: Vec::new(),
1002        });
1003        self
1004    }
1005
1006    /// Add a JOIN with USING clause.
1007    #[must_use]
1008    pub fn join_using(mut self, table_name: &str, columns: &[&str], join_type: JoinType) -> Self {
1009        self.statement.joins.push(JoinClause {
1010            join_type,
1011            table: TableSource::Table(table(table_name, None)),
1012            on: None,
1013            using: columns.iter().map(|s| s.to_string()).collect(),
1014        });
1015        self
1016    }
1017
1018    /// Add a JOIN with a subquery.
1019    #[must_use]
1020    pub fn join_subquery(
1021        mut self,
1022        query: Statement,
1023        alias: &str,
1024        on: &str,
1025        join_type: JoinType,
1026    ) -> Self {
1027        let on_expr = parse_condition_dialect(on, self.dialect);
1028        self.statement.joins.push(JoinClause {
1029            join_type,
1030            table: TableSource::Subquery {
1031                query: Box::new(query),
1032                alias: Some(alias.to_string()),
1033                alias_quote_style: QuoteStyle::None,
1034            },
1035            on: on_expr,
1036            using: Vec::new(),
1037        });
1038        self
1039    }
1040
1041    /// Set the WHERE clause from a string.
1042    #[must_use]
1043    pub fn where_clause(mut self, condition: &str) -> Self {
1044        self.statement.where_clause = parse_condition_dialect(condition, self.dialect);
1045        self
1046    }
1047
1048    /// Set the WHERE clause from an expression.
1049    #[must_use]
1050    pub fn where_expr(mut self, expr: Expr) -> Self {
1051        self.statement.where_clause = Some(expr);
1052        self
1053    }
1054
1055    /// Add to the WHERE clause with AND.
1056    #[must_use]
1057    pub fn and_where(mut self, condition: &str) -> Self {
1058        let new_cond = parse_condition_dialect(condition, self.dialect);
1059        self.statement.where_clause = match (self.statement.where_clause, new_cond) {
1060            (Some(existing), Some(new)) => Some(Expr::BinaryOp {
1061                left: Box::new(existing),
1062                op: BinaryOperator::And,
1063                right: Box::new(new),
1064            }),
1065            (Some(e), None) | (None, Some(e)) => Some(e),
1066            (None, None) => None,
1067        };
1068        self
1069    }
1070
1071    /// Add to the WHERE clause with OR.
1072    #[must_use]
1073    pub fn or_where(mut self, condition: &str) -> Self {
1074        let new_cond = parse_condition_dialect(condition, self.dialect);
1075        self.statement.where_clause = match (self.statement.where_clause, new_cond) {
1076            (Some(existing), Some(new)) => Some(Expr::BinaryOp {
1077                left: Box::new(existing),
1078                op: BinaryOperator::Or,
1079                right: Box::new(new),
1080            }),
1081            (Some(e), None) | (None, Some(e)) => Some(e),
1082            (None, None) => None,
1083        };
1084        self
1085    }
1086
1087    /// Set the GROUP BY clause.
1088    #[must_use]
1089    pub fn group_by(mut self, exprs: &[&str]) -> Self {
1090        self.statement.group_by = exprs
1091            .iter()
1092            .filter_map(|e| parse_expr_dialect(e, self.dialect))
1093            .collect();
1094        self
1095    }
1096
1097    /// Add a GROUP BY expression.
1098    #[must_use]
1099    pub fn add_group_by(mut self, expr: &str) -> Self {
1100        if let Some(e) = parse_expr_dialect(expr, self.dialect) {
1101            self.statement.group_by.push(e);
1102        }
1103        self
1104    }
1105
1106    /// Set the HAVING clause.
1107    #[must_use]
1108    pub fn having(mut self, condition: &str) -> Self {
1109        self.statement.having = parse_condition_dialect(condition, self.dialect);
1110        self
1111    }
1112
1113    /// Set the ORDER BY clause.
1114    #[must_use]
1115    pub fn order_by(mut self, exprs: &[&str]) -> Self {
1116        self.statement.order_by = exprs
1117            .iter()
1118            .filter_map(|e| parse_order_by_item(e, self.dialect))
1119            .collect();
1120        self
1121    }
1122
1123    /// Add an ORDER BY item.
1124    #[must_use]
1125    pub fn add_order_by(mut self, expr: &str) -> Self {
1126        if let Some(item) = parse_order_by_item(expr, self.dialect) {
1127            self.statement.order_by.push(item);
1128        }
1129        self
1130    }
1131
1132    /// Add an ORDER BY item with explicit direction.
1133    #[must_use]
1134    pub fn add_order_by_expr(
1135        mut self,
1136        expr: Expr,
1137        ascending: bool,
1138        nulls_first: Option<bool>,
1139    ) -> Self {
1140        self.statement.order_by.push(OrderByItem {
1141            expr,
1142            ascending,
1143            nulls_first,
1144        });
1145        self
1146    }
1147
1148    /// Set the LIMIT clause.
1149    #[must_use]
1150    pub fn limit(mut self, n: i64) -> Self {
1151        self.statement.limit = Some(Expr::Number(n.to_string()));
1152        self
1153    }
1154
1155    /// Set the LIMIT clause from an expression.
1156    #[must_use]
1157    pub fn limit_expr(mut self, expr: Expr) -> Self {
1158        self.statement.limit = Some(expr);
1159        self
1160    }
1161
1162    /// Set the OFFSET clause.
1163    #[must_use]
1164    pub fn offset(mut self, n: i64) -> Self {
1165        self.statement.offset = Some(Expr::Number(n.to_string()));
1166        self
1167    }
1168
1169    /// Set the OFFSET clause from an expression.
1170    #[must_use]
1171    pub fn offset_expr(mut self, expr: Expr) -> Self {
1172        self.statement.offset = Some(expr);
1173        self
1174    }
1175
1176    /// Set TOP N (T-SQL style).
1177    #[must_use]
1178    pub fn top(mut self, n: i64) -> Self {
1179        self.statement.top = Some(Box::new(Expr::Number(n.to_string())));
1180        self
1181    }
1182
1183    /// Set the QUALIFY clause (BigQuery, Snowflake).
1184    #[must_use]
1185    pub fn qualify(mut self, condition: &str) -> Self {
1186        self.statement.qualify = parse_condition_dialect(condition, self.dialect);
1187        self
1188    }
1189
1190    /// Build the final SELECT statement.
1191    #[must_use]
1192    pub fn build(self) -> Statement {
1193        Statement::Select(self.statement)
1194    }
1195
1196    /// Build and return the inner SelectStatement.
1197    #[must_use]
1198    pub fn build_select(self) -> SelectStatement {
1199        self.statement
1200    }
1201}
1202
1203/// Create a new SELECT builder with columns.
1204///
1205/// # Examples
1206///
1207/// ```rust
1208/// use sqlglot_rust::builder::select;
1209///
1210/// let query = select(&["a", "b", "c"]).from("table_name").build();
1211/// ```
1212#[must_use]
1213pub fn select(columns: &[&str]) -> SelectBuilder {
1214    SelectBuilder::new().columns(columns)
1215}
1216
1217/// Create a SELECT * query.
1218///
1219/// # Examples
1220///
1221/// ```rust
1222/// use sqlglot_rust::builder::select_all;
1223///
1224/// let query = select_all().from("users").build();
1225/// ```
1226#[must_use]
1227pub fn select_all() -> SelectBuilder {
1228    SelectBuilder::new().all()
1229}
1230
1231/// Create a SELECT DISTINCT builder.
1232///
1233/// # Examples
1234///
1235/// ```rust
1236/// use sqlglot_rust::builder::select_distinct;
1237///
1238/// let query = select_distinct(&["category"]).from("products").build();
1239/// ```
1240#[must_use]
1241pub fn select_distinct(columns: &[&str]) -> SelectBuilder {
1242    SelectBuilder::new().distinct().columns(columns)
1243}
1244
1245// ═══════════════════════════════════════════════════════════════════════
1246// Statement Mutation Methods
1247// ═══════════════════════════════════════════════════════════════════════
1248
1249impl SelectStatement {
1250    /// Add a column to the SELECT list.
1251    ///
1252    /// # Examples
1253    ///
1254    /// ```rust
1255    /// use sqlglot_rust::builder::select;
1256    ///
1257    /// let mut stmt = select(&["a"]).from("t").build_select();
1258    /// stmt.add_select("b");
1259    /// ```
1260    pub fn add_select(&mut self, expr_str: &str) {
1261        self.add_select_dialect(expr_str, Dialect::Ansi);
1262    }
1263
1264    /// Add a column with dialect-specific parsing.
1265    pub fn add_select_dialect(&mut self, expr_str: &str, dialect: Dialect) {
1266        if let Some(expr) = parse_expr_dialect(expr_str, dialect) {
1267            self.columns.push(SelectItem::Expr { expr, alias: None, alias_quote_style: QuoteStyle::None });
1268        }
1269    }
1270
1271    /// Add an expression to the SELECT list.
1272    pub fn add_select_expr(&mut self, expr: Expr, alias: Option<&str>) {
1273        self.columns.push(SelectItem::Expr {
1274            expr,
1275            alias: alias.map(String::from),
1276            alias_quote_style: QuoteStyle::None,
1277        });
1278    }
1279
1280    /// Add a condition to the WHERE clause (AND).
1281    ///
1282    /// # Examples
1283    ///
1284    /// ```rust
1285    /// use sqlglot_rust::builder::select;
1286    ///
1287    /// let mut stmt = select(&["a"]).from("t").build_select();
1288    /// stmt.add_where("x > 1");
1289    /// stmt.add_where("y < 10");
1290    /// ```
1291    pub fn add_where(&mut self, condition: &str) {
1292        self.add_where_dialect(condition, Dialect::Ansi);
1293    }
1294
1295    /// Add a WHERE condition with dialect-specific parsing.
1296    pub fn add_where_dialect(&mut self, condition: &str, dialect: Dialect) {
1297        let new_cond = parse_condition_dialect(condition, dialect);
1298        self.where_clause = match (self.where_clause.take(), new_cond) {
1299            (Some(existing), Some(new)) => Some(Expr::BinaryOp {
1300                left: Box::new(existing),
1301                op: BinaryOperator::And,
1302                right: Box::new(new),
1303            }),
1304            (Some(e), None) | (None, Some(e)) => Some(e),
1305            (None, None) => None,
1306        };
1307    }
1308
1309    /// Add an expression to the WHERE clause (AND).
1310    pub fn add_where_expr(&mut self, expr: Expr) {
1311        self.where_clause = match self.where_clause.take() {
1312            Some(existing) => Some(Expr::BinaryOp {
1313                left: Box::new(existing),
1314                op: BinaryOperator::And,
1315                right: Box::new(expr),
1316            }),
1317            None => Some(expr),
1318        };
1319    }
1320
1321    /// Add a JOIN clause.
1322    ///
1323    /// # Examples
1324    ///
1325    /// ```rust
1326    /// use sqlglot_rust::builder::select;
1327    /// use sqlglot_rust::ast::JoinType;
1328    ///
1329    /// let mut stmt = select(&["*"]).from("users").build_select();
1330    /// stmt.add_join("orders", "users.id = orders.user_id", JoinType::Left);
1331    /// ```
1332    pub fn add_join(&mut self, table_name: &str, on: &str, join_type: JoinType) {
1333        self.add_join_dialect(table_name, on, join_type, Dialect::Ansi);
1334    }
1335
1336    /// Add a JOIN with dialect-specific parsing.
1337    pub fn add_join_dialect(
1338        &mut self,
1339        table_name: &str,
1340        on: &str,
1341        join_type: JoinType,
1342        dialect: Dialect,
1343    ) {
1344        let on_expr = parse_condition_dialect(on, dialect);
1345        self.joins.push(JoinClause {
1346            join_type,
1347            table: TableSource::Table(table(table_name, None)),
1348            on: on_expr,
1349            using: Vec::new(),
1350        });
1351    }
1352
1353    /// Add a JOIN with a subquery.
1354    pub fn add_join_subquery(
1355        &mut self,
1356        query: Statement,
1357        alias: &str,
1358        on: &str,
1359        join_type: JoinType,
1360    ) {
1361        self.add_join_subquery_dialect(query, alias, on, join_type, Dialect::Ansi);
1362    }
1363
1364    /// Add a JOIN with a subquery and dialect-specific parsing.
1365    pub fn add_join_subquery_dialect(
1366        &mut self,
1367        query: Statement,
1368        alias: &str,
1369        on: &str,
1370        join_type: JoinType,
1371        dialect: Dialect,
1372    ) {
1373        let on_expr = parse_condition_dialect(on, dialect);
1374        self.joins.push(JoinClause {
1375            join_type,
1376            table: TableSource::Subquery {
1377                query: Box::new(query),
1378                alias: Some(alias.to_string()),
1379                alias_quote_style: QuoteStyle::None,
1380            },
1381            on: on_expr,
1382            using: Vec::new(),
1383        });
1384    }
1385
1386    /// Wrap this SELECT as a subquery with an alias.
1387    ///
1388    /// # Examples
1389    ///
1390    /// ```rust
1391    /// use sqlglot_rust::builder::select;
1392    ///
1393    /// let inner = select(&["id", "name"]).from("users").build_select();
1394    /// let subq = inner.as_subquery("u");
1395    /// ```
1396    #[must_use]
1397    pub fn as_subquery(self, alias: &str) -> TableSource {
1398        TableSource::Subquery {
1399            query: Box::new(Statement::Select(self)),
1400            alias: Some(alias.to_string()),
1401            alias_quote_style: QuoteStyle::None,
1402        }
1403    }
1404}
1405
1406// ═══════════════════════════════════════════════════════════════════════
1407// Helper functions
1408// ═══════════════════════════════════════════════════════════════════════
1409
1410/// Parse an ORDER BY item string like "col ASC" or "col DESC NULLS FIRST"
1411fn parse_order_by_item(s: &str, dialect: Dialect) -> Option<OrderByItem> {
1412    let s = s.trim();
1413    let upper = s.to_uppercase();
1414
1415    // Check for NULLS FIRST/LAST
1416    let nulls_first = if upper.contains("NULLS FIRST") {
1417        Some(true)
1418    } else if upper.contains("NULLS LAST") {
1419        Some(false)
1420    } else {
1421        None
1422    };
1423
1424    // Remove NULLS clause for parsing
1425    let s = s
1426        .replace("NULLS FIRST", "")
1427        .replace("NULLS LAST", "")
1428        .replace("nulls first", "")
1429        .replace("nulls last", "");
1430    let s = s.trim();
1431
1432    // Check for ASC/DESC
1433    let (expr_str, ascending) = if s.to_uppercase().ends_with(" DESC") {
1434        (&s[..s.len() - 5], false)
1435    } else if s.to_uppercase().ends_with(" ASC") {
1436        (&s[..s.len() - 4], true)
1437    } else {
1438        (s, true)
1439    };
1440
1441    parse_expr_dialect(expr_str.trim(), dialect).map(|expr| OrderByItem {
1442        expr,
1443        ascending,
1444        nulls_first,
1445    })
1446}
1447
1448#[cfg(test)]
1449mod tests {
1450    use super::*;
1451    use crate::generate;
1452
1453    #[test]
1454    fn test_column() {
1455        let col = column("name", None);
1456        assert!(
1457            matches!(col, Expr::Column { name, table, .. } if name == "name" && table.is_none())
1458        );
1459
1460        let qualified = column("id", Some("users"));
1461        assert!(matches!(qualified, Expr::Column { name, table, .. }
1462            if name == "id" && table == Some("users".to_string())));
1463    }
1464
1465    #[test]
1466    fn test_table() {
1467        let tbl = table("users", None);
1468        assert_eq!(tbl.name, "users");
1469        assert!(tbl.schema.is_none());
1470
1471        let qualified = table("orders", Some("public"));
1472        assert_eq!(qualified.name, "orders");
1473        assert_eq!(qualified.schema, Some("public".to_string()));
1474    }
1475
1476    #[test]
1477    fn test_literals() {
1478        assert!(matches!(literal(42), Expr::Number(n) if n == "42"));
1479        assert!(matches!(string_literal("hello"), Expr::StringLiteral(s) if s == "hello"));
1480        assert!(matches!(boolean(true), Expr::Boolean(true)));
1481        assert!(matches!(null(), Expr::Null));
1482    }
1483
1484    #[test]
1485    fn test_cast() {
1486        let col = column("id", None);
1487        let casted = cast(col, DataType::BigInt);
1488        assert!(matches!(
1489            casted,
1490            Expr::Cast {
1491                data_type: DataType::BigInt,
1492                ..
1493            }
1494        ));
1495    }
1496
1497    #[test]
1498    fn test_and_all() {
1499        let cond1 = eq(column("x", None), literal(1));
1500        let cond2 = eq(column("y", None), literal(2));
1501
1502        let combined = and_all(vec![cond1, cond2]).unwrap();
1503        assert!(matches!(
1504            combined,
1505            Expr::BinaryOp {
1506                op: BinaryOperator::And,
1507                ..
1508            }
1509        ));
1510
1511        // Empty returns None
1512        assert!(and_all(Vec::<Expr>::new()).is_none());
1513    }
1514
1515    #[test]
1516    fn test_or_all() {
1517        let cond1 = eq(column("x", None), literal(1));
1518        let cond2 = eq(column("y", None), literal(2));
1519
1520        let combined = or_all(vec![cond1, cond2]).unwrap();
1521        assert!(matches!(
1522            combined,
1523            Expr::BinaryOp {
1524                op: BinaryOperator::Or,
1525                ..
1526            }
1527        ));
1528    }
1529
1530    #[test]
1531    fn test_parse_expr() {
1532        let expr = parse_expr("x + 1").unwrap();
1533        assert!(matches!(
1534            expr,
1535            Expr::BinaryOp {
1536                op: BinaryOperator::Plus,
1537                ..
1538            }
1539        ));
1540    }
1541
1542    #[test]
1543    fn test_parse_condition() {
1544        let cond = parse_condition("x > 1 AND y < 10").unwrap();
1545        assert!(matches!(
1546            cond,
1547            Expr::BinaryOp {
1548                op: BinaryOperator::And,
1549                ..
1550            }
1551        ));
1552    }
1553
1554    #[test]
1555    fn test_condition_builder() {
1556        let cond = condition("x = 1").and("y = 2").or("z = 3").build();
1557        assert!(cond.is_some());
1558    }
1559
1560    #[test]
1561    fn test_condition_builder_not() {
1562        let cond = condition("x = 1").not().build().unwrap();
1563        assert!(matches!(
1564            cond,
1565            Expr::UnaryOp {
1566                op: crate::ast::UnaryOperator::Not,
1567                ..
1568            }
1569        ));
1570    }
1571
1572    #[test]
1573    fn test_select_builder_basic() {
1574        let query = select(&["a", "b"]).from("users").build();
1575        let sql = generate(&query, Dialect::Ansi);
1576        assert!(sql.contains("SELECT"));
1577        assert!(sql.contains("a"));
1578        assert!(sql.contains("b"));
1579        assert!(sql.contains("FROM users"));
1580    }
1581
1582    #[test]
1583    fn test_select_builder_where() {
1584        let query = select(&["*"])
1585            .from("users")
1586            .where_clause("active = true")
1587            .build();
1588        let sql = generate(&query, Dialect::Ansi);
1589        assert!(sql.contains("WHERE"));
1590    }
1591
1592    #[test]
1593    fn test_select_builder_join() {
1594        let query = select(&["u.name", "o.total"])
1595            .from("users")
1596            .join("orders", "users.id = orders.user_id")
1597            .build();
1598        let sql = generate(&query, Dialect::Ansi);
1599        assert!(sql.contains("JOIN"));
1600    }
1601
1602    #[test]
1603    fn test_select_builder_group_by() {
1604        let query = select(&["category", "COUNT(*)"])
1605            .from("products")
1606            .group_by(&["category"])
1607            .having("COUNT(*) > 5")
1608            .build();
1609        let sql = generate(&query, Dialect::Ansi);
1610        assert!(sql.contains("GROUP BY"));
1611        assert!(sql.contains("HAVING"));
1612    }
1613
1614    #[test]
1615    fn test_select_builder_order_limit() {
1616        let query = select(&["*"])
1617            .from("users")
1618            .order_by(&["created_at DESC"])
1619            .limit(10)
1620            .offset(5)
1621            .build();
1622        let sql = generate(&query, Dialect::Ansi);
1623        assert!(sql.contains("ORDER BY"));
1624        assert!(sql.contains("LIMIT 10"));
1625        assert!(sql.contains("OFFSET 5"));
1626    }
1627
1628    #[test]
1629    fn test_select_builder_distinct() {
1630        let query = select_distinct(&["category"]).from("products").build();
1631        let sql = generate(&query, Dialect::Ansi);
1632        assert!(sql.contains("SELECT DISTINCT"));
1633    }
1634
1635    #[test]
1636    fn test_select_all() {
1637        let query = select_all().from("users").build();
1638        let sql = generate(&query, Dialect::Ansi);
1639        assert!(sql.contains("SELECT *"));
1640    }
1641
1642    #[test]
1643    fn test_mutation_add_select() {
1644        let mut stmt = select(&["a"]).from("t").build_select();
1645        stmt.add_select("b");
1646        assert_eq!(stmt.columns.len(), 2);
1647    }
1648
1649    #[test]
1650    fn test_mutation_add_where() {
1651        let mut stmt = select(&["*"]).from("t").build_select();
1652        stmt.add_where("x > 1");
1653        stmt.add_where("y < 10");
1654        // Should be combined with AND
1655        assert!(stmt.where_clause.is_some());
1656    }
1657
1658    #[test]
1659    fn test_mutation_add_join() {
1660        let mut stmt = select(&["*"]).from("users").build_select();
1661        stmt.add_join("orders", "users.id = orders.user_id", JoinType::Inner);
1662        assert_eq!(stmt.joins.len(), 1);
1663    }
1664
1665    #[test]
1666    fn test_as_subquery() {
1667        let inner = select(&["id"]).from("users").build_select();
1668        let source = inner.as_subquery("u");
1669        assert!(matches!(source, TableSource::Subquery { alias: Some(a), .. } if a == "u"));
1670    }
1671
1672    #[test]
1673    fn test_comparison_helpers() {
1674        let e = eq(column("a", None), literal(1));
1675        assert!(matches!(
1676            e,
1677            Expr::BinaryOp {
1678                op: BinaryOperator::Eq,
1679                ..
1680            }
1681        ));
1682
1683        let e = neq(column("a", None), literal(1));
1684        assert!(matches!(
1685            e,
1686            Expr::BinaryOp {
1687                op: BinaryOperator::Neq,
1688                ..
1689            }
1690        ));
1691
1692        let e = lt(column("a", None), literal(1));
1693        assert!(matches!(
1694            e,
1695            Expr::BinaryOp {
1696                op: BinaryOperator::Lt,
1697                ..
1698            }
1699        ));
1700
1701        let e = gt(column("a", None), literal(1));
1702        assert!(matches!(
1703            e,
1704            Expr::BinaryOp {
1705                op: BinaryOperator::Gt,
1706                ..
1707            }
1708        ));
1709    }
1710
1711    #[test]
1712    fn test_arithmetic_helpers() {
1713        let e = add(column("a", None), literal(1));
1714        assert!(matches!(
1715            e,
1716            Expr::BinaryOp {
1717                op: BinaryOperator::Plus,
1718                ..
1719            }
1720        ));
1721
1722        let e = mul(column("a", None), literal(2));
1723        assert!(matches!(
1724            e,
1725            Expr::BinaryOp {
1726                op: BinaryOperator::Multiply,
1727                ..
1728            }
1729        ));
1730    }
1731
1732    #[test]
1733    fn test_is_null_helpers() {
1734        let e = is_null(column("a", None));
1735        assert!(matches!(e, Expr::IsNull { negated: false, .. }));
1736
1737        let e = is_not_null(column("a", None));
1738        assert!(matches!(e, Expr::IsNull { negated: true, .. }));
1739    }
1740
1741    #[test]
1742    fn test_between() {
1743        let e = between(column("x", None), literal(1), literal(10));
1744        assert!(matches!(e, Expr::Between { negated: false, .. }));
1745    }
1746
1747    #[test]
1748    fn test_in_list() {
1749        let e = in_list(
1750            column("status", None),
1751            vec![string_literal("active"), string_literal("pending")],
1752        );
1753        assert!(matches!(e, Expr::InList { negated: false, .. }));
1754    }
1755
1756    #[test]
1757    fn test_like() {
1758        let e = like(column("name", None), string_literal("%John%"));
1759        assert!(matches!(e, Expr::Like { negated: false, .. }));
1760    }
1761
1762    #[test]
1763    fn test_func() {
1764        let f = func("UPPER", vec![column("name", None)]);
1765        assert!(matches!(f, Expr::Function { name, distinct: false, .. } if name == "UPPER"));
1766    }
1767
1768    #[test]
1769    fn test_func_distinct() {
1770        let f = func_distinct("COUNT", vec![column("id", None)]);
1771        assert!(matches!(f, Expr::Function { name, distinct: true, .. } if name == "COUNT"));
1772    }
1773
1774    #[test]
1775    fn test_alias() {
1776        let e = alias(column("first_name", None), "name");
1777        assert!(matches!(e, Expr::Alias { name, .. } if name == "name"));
1778    }
1779
1780    #[test]
1781    fn test_subquery_and_exists() {
1782        let inner = select(&["1"]).from("users").where_clause("id = 1").build();
1783        let sub = subquery(inner.clone());
1784        assert!(matches!(sub, Expr::Subquery(_)));
1785
1786        let ex = exists(inner, false);
1787        assert!(matches!(ex, Expr::Exists { negated: false, .. }));
1788    }
1789
1790    #[test]
1791    fn test_star_and_qualified_star() {
1792        let s = star();
1793        assert!(matches!(s, Expr::Star));
1794
1795        let qs = qualified_star("users");
1796        assert!(matches!(qs, Expr::QualifiedWildcard { table } if table == "users"));
1797    }
1798
1799    #[test]
1800    fn test_complex_query() {
1801        // Build a more complex query
1802        let query = select(&["u.id", "u.name", "COUNT(o.id) AS order_count"])
1803            .from("users")
1804            .join("orders", "u.id = o.user_id")
1805            .where_clause("u.active = true")
1806            .and_where("o.created_at > '2024-01-01'")
1807            .group_by(&["u.id", "u.name"])
1808            .having("COUNT(o.id) > 0")
1809            .order_by(&["order_count DESC"])
1810            .limit(10)
1811            .build();
1812
1813        let sql = generate(&query, Dialect::Postgres);
1814        assert!(sql.contains("SELECT"));
1815        assert!(sql.contains("JOIN"));
1816        assert!(sql.contains("WHERE"));
1817        assert!(sql.contains("GROUP BY"));
1818        assert!(sql.contains("HAVING"));
1819        assert!(sql.contains("ORDER BY"));
1820        assert!(sql.contains("LIMIT"));
1821    }
1822
1823    #[test]
1824    fn test_subquery_in_from() {
1825        let inner = select(&["id", "name"])
1826            .from("users")
1827            .where_clause("active = true")
1828            .build();
1829        let outer = select(&["*"]).from_subquery(inner, "active_users").build();
1830
1831        let sql = generate(&outer, Dialect::Ansi);
1832        assert!(sql.contains("FROM (SELECT"));
1833        assert!(sql.contains(") AS active_users"));
1834    }
1835}