Skip to main content

sqlmodel_query/
clause.rs

1//! SQL clause types (WHERE, ORDER BY, LIMIT, etc.)
2
3use crate::expr::{Dialect, Expr};
4use sqlmodel_core::Value;
5
6/// WHERE clause.
7#[derive(Debug, Clone)]
8pub struct Where {
9    expr: Expr,
10}
11
12impl Where {
13    /// Create a new WHERE clause with the given expression.
14    pub fn new(expr: Expr) -> Self {
15        Self { expr }
16    }
17
18    /// Add an AND condition.
19    pub fn and(self, expr: Expr) -> Self {
20        Self {
21            expr: self.expr.and(expr),
22        }
23    }
24
25    /// Add an OR condition.
26    pub fn or(self, expr: Expr) -> Self {
27        Self {
28            expr: self.expr.or(expr),
29        }
30    }
31
32    /// Build the WHERE clause SQL and parameters with default dialect (Postgres).
33    pub fn build(&self) -> (String, Vec<Value>) {
34        self.build_with_dialect(Dialect::default(), 0)
35    }
36
37    /// Build the WHERE clause with a parameter offset and default dialect.
38    pub fn build_with_offset(&self, offset: usize) -> (String, Vec<Value>) {
39        self.build_with_dialect(Dialect::default(), offset)
40    }
41
42    /// Build the WHERE clause with a specific dialect and offset.
43    pub fn build_with_dialect(&self, dialect: Dialect, offset: usize) -> (String, Vec<Value>) {
44        let mut params = Vec::new();
45        let sql = self.expr.build_with_dialect(dialect, &mut params, offset);
46        (sql, params)
47    }
48}
49
50/// ORDER BY clause.
51#[derive(Debug, Clone)]
52pub struct OrderBy {
53    pub expr: Expr,
54    pub direction: OrderDirection,
55    pub nulls: Option<NullsOrder>,
56}
57
58/// Sort direction.
59#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
60pub enum OrderDirection {
61    #[default]
62    Asc,
63    Desc,
64}
65
66/// NULLS FIRST/LAST ordering.
67#[derive(Debug, Clone, Copy, PartialEq, Eq)]
68pub enum NullsOrder {
69    First,
70    Last,
71}
72
73impl OrderBy {
74    /// Create an ascending order by clause.
75    pub fn asc(expr: impl Into<Expr>) -> Self {
76        Self {
77            expr: expr.into(),
78            direction: OrderDirection::Asc,
79            nulls: None,
80        }
81    }
82
83    /// Create a descending order by clause.
84    pub fn desc(expr: impl Into<Expr>) -> Self {
85        Self {
86            expr: expr.into(),
87            direction: OrderDirection::Desc,
88            nulls: None,
89        }
90    }
91
92    /// Set NULLS FIRST.
93    pub fn nulls_first(mut self) -> Self {
94        self.nulls = Some(NullsOrder::First);
95        self
96    }
97
98    /// Set NULLS LAST.
99    pub fn nulls_last(mut self) -> Self {
100        self.nulls = Some(NullsOrder::Last);
101        self
102    }
103
104    /// Build SQL for this ORDER BY clause.
105    pub fn build(&self, dialect: Dialect, params: &mut Vec<Value>, offset: usize) -> String {
106        let mut sql = self.expr.build_with_dialect(dialect, params, offset);
107
108        sql.push_str(match self.direction {
109            OrderDirection::Asc => " ASC",
110            OrderDirection::Desc => " DESC",
111        });
112
113        if let Some(nulls) = self.nulls {
114            sql.push_str(match nulls {
115                NullsOrder::First => " NULLS FIRST",
116                NullsOrder::Last => " NULLS LAST",
117            });
118        }
119
120        sql
121    }
122}
123
124/// LIMIT clause.
125#[derive(Debug, Clone, Copy)]
126pub struct Limit(pub u64);
127
128/// OFFSET clause.
129#[derive(Debug, Clone, Copy)]
130pub struct Offset(pub u64);
131
132/// GROUP BY clause helper.
133#[derive(Debug, Clone)]
134pub struct GroupBy {
135    columns: Vec<String>,
136}
137
138impl GroupBy {
139    /// Create a new GROUP BY clause.
140    pub fn new(columns: &[&str]) -> Self {
141        Self {
142            columns: columns.iter().map(|&s| s.to_string()).collect(),
143        }
144    }
145
146    /// Generate SQL for this GROUP BY clause.
147    pub fn to_sql(&self) -> String {
148        self.columns.join(", ")
149    }
150}