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