Skip to main content

ngb_sqlbuilder/
select.rs

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