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