Skip to main content

ngb_sqlbuilder/
select.rs

1use crate::*;
2use tokio_postgres::types::ToSql;
3
4pub struct Where;
5pub struct Order;
6pub struct SelectCondition<'q> {
7    pub(crate) sql: String,
8    pub(crate) params: Vec<&'q (dyn ToSql + Sync)>,
9}
10
11impl<'q, T> SqlClause<'q> for Clause<'q, T> {
12    fn unwrap(self) -> (String, Vec<&'q (dyn ToSql + Sync)>) {
13        (self.sql, self.params)
14    }
15}
16
17impl<'q> Clause<'q, Where> {
18    pub fn limit(self, limit: &'q i64) -> Clause<'q, Paging> {
19        __limit(self, limit)
20    }
21    pub fn offset(self, offset: &'q i64) -> Clause<'q, Paging> {
22        __offset(self, offset)
23    }
24}
25
26impl<'q> Clause<'q, Paging> {
27    pub fn limit(self, limit: &'q i64) -> Clause<'q, ()> {
28        __limit(self, limit)
29    }
30    pub fn offset(self, offset: &'q i64) -> Clause<'q, ()> {
31        __offset(self, offset)
32    }
33}
34
35impl<'q> Clause<'q, Order> {
36    pub fn limit(self, limit: &'q i64) -> Clause<'q, Paging> {
37        __limit(self, limit)
38    }
39    pub fn offset(self, offset: &'q i64) -> Clause<'q, Paging> {
40        __offset(self, offset)
41    }
42}
43
44impl<'q> Clause<'q, Select> {
45    pub fn select<T>(self, clause: Clause<'q, T>) -> Clause<'q, Select> {
46        let mut sql = self.sql;
47        let mut params = self.params;
48        sql.push_str(", ");
49        sql.push_str(&clause.sql);
50        params.extend_from_slice(&clause.params);
51        Clause::new(sql, params)
52    }
53    pub fn from(self, table: &str) -> Clause<'q, From> {
54        let mut sql = self.sql;
55        sql.push_str(" FROM ");
56        sql.push_str(&format_prim(table));
57        Clause::new(sql, self.params)
58    }
59}
60
61fn __join<'q, T>(clause: Clause<'q, T>, join: &str, table: &str) -> Clause<'q, Join> {
62    let (mut sql, params) = clause.unwrap();
63    sql.push_str(&format!(" {join}JOIN "));
64    sql.push_str(&format_prim(table));
65    Clause::new(sql, params)
66}
67impl<'q> Clause<'q, From> {
68    pub fn join(self, table: &str) -> Clause<'q, Join> {
69        __join(self, "", table)
70    }
71    pub fn left_join(self, table: &str) -> Clause<'q, Join> {
72        __join(self, "LEFT ", table)
73    }
74
75    pub fn left_outer_join(self, table: &str) -> Clause<'q, Join> {
76        __join(self, "LEFT OUTER ", table)
77    }
78
79    pub fn right_join(self, table: &str) -> Clause<'q, Join> {
80        __join(self, "RIGHT ", table)
81    }
82
83    pub fn right_outer_join(self, table: &str) -> Clause<'q, Join> {
84        __join(self, "RIGHT OUTER ", table)
85    }
86
87    pub fn where_col(self, name: &str, condition: Clause<'q, Condition>) -> Clause<'q, Where> {
88        __where(self, name, condition)
89    }
90    pub fn where_cond(self, condition: Clause<'q, Condition>) -> Clause<'q, Where> {
91        let (mut sql, params) = self.unwrap();
92        sql.push_str(" WHERE ");
93        __condition(Clause::<From>::new(sql, params), condition)
94    }
95
96    pub fn limit(self, limit: &'q i64) -> Clause<'q, ()> {
97        __limit(self, limit)
98    }
99    pub fn offset(self, offset: &'q i64) -> Clause<'q, ()> {
100        __offset(self, offset)
101    }
102}
103fn __limit<'q, T, C>(clause: Clause<'q, T>, limit: &'q i64) -> Clause<'q, C> {
104    let (mut sql, mut params) = clause.unwrap();
105    sql.push_str(" LIMIT $");
106    params.push(limit);
107    Clause::new(sql, params)
108}
109fn __offset<'q, T, C>(clause: Clause<'q, T>, offset: &'q i64) -> Clause<'q, C> {
110    let (mut sql, mut params) = clause.unwrap();
111    sql.push_str(" OFFSET $");
112    params.push(offset);
113    Clause::new(sql, params)
114}
115
116impl<'q> Clause<'q, Join> {
117    pub fn using(self, cols: Clause<Column>) -> Clause<'q, From> {
118        let (mut sql, params) = self.unwrap();
119        let (col_sql, _) = cols.unwrap();
120        sql.push_str(" using(");
121        sql.push_str(&col_sql);
122        sql.push(')');
123        Clause::new(sql, params)
124    }
125    pub fn using_col(self, col_name: &str) -> Clause<'q, From> {
126        let (mut sql, params) = self.unwrap();
127        sql.push_str(" using(");
128        sql.push_str(&format_col(col_name));
129        sql.push(')');
130        Clause::new(sql, params)
131    }
132    pub fn using_cols(self, col_names: &[&str]) -> Clause<'q, From> {
133        let (mut sql, params) = self.unwrap();
134        sql.push_str(" using(");
135        __cols(&mut sql, col_names);
136        sql.push(')');
137        Clause::new(sql, params)
138    }
139    pub fn on(self, col1: &str, col2: &str) -> Clause<'q, From> {
140        let (mut sql, params) = self.unwrap();
141        sql.push_str(" on ");
142        sql.push_str(&format_col(col1));
143        sql.push_str(" = ");
144        sql.push_str(&format_col(col2));
145        Clause::new(sql, params)
146    }
147    pub fn on_wrap(self, condition: Clause<'q, Condition>) -> Clause<'q, From> {
148        let (mut sql, params) = self.unwrap();
149        let (cond_sql, _) = condition.unwrap();
150        sql.push_str(" on (");
151        sql.push_str(&cond_sql);
152        sql.push(')');
153        Clause::new(sql, params)
154    }
155}
156
157pub(crate) fn __where<'q, T, C>(
158    clause: Clause<'q, T>,
159    name: &str,
160    condition: Clause<'q, Condition>,
161) -> Clause<'q, C> {
162    let mut sql = clause.sql;
163    let mut params = clause.params;
164    sql.push_str(" where ");
165    sql.push_str(&format_col(name));
166    let (cond_sql, cond_params) = condition.unwrap();
167    sql.push_str(cond_sql.as_str());
168    params.extend(cond_params);
169    Clause::new(sql, params)
170}
171
172impl<'q> Clause<'q, Where> {
173    pub fn and(self, name: &str, condition: Clause<'q, Condition>) -> Clause<'q, Where> {
174        __and_col(self, name, condition)
175    }
176    pub fn or(self, name: &str, condition: Clause<'q, Condition>) -> Clause<'q, Where> {
177        __or_col(self, name, condition)
178    }
179
180    pub fn and_wrap(self, clause: Clause<'q, Where>) -> Clause<'q, Where> {
181        __and_wrap(self, clause)
182    }
183
184    pub fn or_wrap(self, clause: Clause<'q, Where>) -> Clause<'q, Where> {
185        __or_wrap(self, clause)
186    }
187
188    pub fn order_by_cols(self, columns: &[&str]) -> Clause<'q, Order> {
189        let (mut sql, params) = self.unwrap();
190        let mut not_first = false;
191        sql.push_str(" order by ");
192        for col in columns {
193            if not_first {
194                sql.push_str(", ");
195            } else {
196                not_first = true;
197            }
198            sql.push_str(&__order(col));
199        }
200        Clause::new(sql, params)
201    }
202}
203fn __order(col: &str) -> String {
204    let (col_name, dir) = {
205        let mut split_as = col.split(" ");
206        let col_name = split_as.next().unwrap_or_default();
207        let alias = split_as.next();
208        (wrap(col_name), alias)
209    };
210    if let Some(dir) = dir
211        && dir.eq_ignore_ascii_case("desc")
212    {
213        format!("{} {}", col_name, dir)
214    } else {
215        format!("{}", col_name)
216    }
217}
218
219// pub trait FromClause {}
220
221pub fn select(clause: Clause<Column>) -> Clause<Select> {
222    Clause::new(format!("select {}", clause.sql), clause.params)
223}
224
225pub fn select_all<'q>() -> Clause<'q, Select> {
226    Clause::new("select *".to_string(), Vec::new())
227}
228
229pub fn col<'q>(name: &str) -> Clause<'q, Column> {
230    Clause::new(format_prim(name), vec![])
231}
232
233pub fn val<'q, T: ToSql + Sync + 'q, S>(value: &'q T) -> Clause<'q, S> {
234    Clause::new("$".to_string(), vec![value])
235}
236pub fn where_col<'q>(name: &str, condition: Clause<'q, Condition>) -> Clause<'q, Where> {
237    let mut sql = String::new();
238    let (cond_sql, cond_params) = condition.unwrap();
239    sql.push(' ');
240    sql.push_str(&format_col(name));
241    sql.push_str(cond_sql.as_str());
242    Clause::new(sql, cond_params)
243}
244
245#[cfg(test)]
246mod tests {
247    use super::*;
248
249    #[test]
250    fn example() {
251        let user_id: i32 = 123;
252        let limit: i64 = 10;
253        let offset: i64 = 50;
254        let (sql, params) = select(cols(&[
255            "u.UserId as Id",
256            "u.Name",
257            "p.Title",
258            "p.Content::text as Body",
259        ]))
260        .from("User as u")
261        .left_join("Post as p")
262        .using_col("UserId")
263        .where_col("u.UserId", eq(&user_id).cast_raw("bigint"))
264        .order_by_cols(&["CreatedAt desc"])
265        .limit(&limit)
266        .offset(&offset)
267        .build();
268
269        println!("SQL: {}", sql);
270        println!("Params: {:?}", params);
271        // let rows = client.query(&sql, &params).await.unwrap();
272    }
273    #[test]
274    fn select_test() {
275        let user_id: i32 = 123;
276        let (sql, params) = select(cols(&["u.UserId", "u.Name", "p.Title", "p.Content"]))
277            .from("User as u")
278            .left_join("Post as p")
279            .using_col("UserId")
280            .where_col("u.UserId", eq(&user_id))
281            .and("t.Value", lt(&1000))
282            .and_wrap(where_col("t.Asd", eq(&true)).or("Xwe", eq(&false)))
283            .order_by_cols(&["CreatedAt desc"])
284            .limit(&10)
285            .offset(&100)
286            .build();
287        // let column = col("a.foo as dfg");
288        println!("{}", sql);
289        println!("{:?}", params);
290    }
291
292    #[test]
293    fn nested() {
294        let q = select(cols(&["u.UserId", "u.Name"]))
295            .from("User as u")
296            .where_cond(ands(&[
297                col("u.UserId").eq(&123),
298                ors(&[col("u.UserId").eq(&456), col_op("u.Id", ">=", &789)]),
299            ]))
300            .build();
301        println!("{}", q.0);
302    }
303
304    #[test]
305    fn branched() {
306        let opt1 = Some(1);
307        let mut query = select(cols(&["u.UserId", "u.Name", "p.Title", "p.Content"]))
308            .from("User as u")
309            .where_col("u.UserId", eq(&opt1));
310        if let Some(val) = &opt1 {
311            query = query.and("u.PostId", eq(val));
312        }
313        query.and("u.Title", eq(&1));
314    }
315}