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