Skip to main content

ngb_sqlbuilder/
select.rs

1#![allow(clippy::wrong_self_convention)]
2use crate::*;
3use tokio_postgres::types::ToSql;
4
5pub struct Where;
6pub struct Order;
7pub struct Group;
8pub struct SelectCondition<'q> {
9    pub(crate) sql: String,
10    pub(crate) params: Vec<&'q (dyn ToSql + Sync)>,
11}
12
13impl<'q, T> SqlClause<'q> for Clause<'q, T> {
14    fn unwrap(self) -> (String, Vec<&'q (dyn ToSql + Sync)>) {
15        (self.sql, self.params)
16    }
17
18    fn unwrap_ref(&self) -> (&str, &[&'q (dyn ToSql + Sync)]) {
19        (&self.sql, &self.params)
20    }
21}
22fn __group_by<'q, T1, T2, T3>(clause: Clause<'q, T1>, group: Clause<'_, T2>) -> Clause<'q, T3> {
23    let (mut sql, params) = clause.unwrap();
24    sql.push_str(" GROUP BY (");
25    let (cols, _) = group.unwrap();
26    sql.push_str(&cols);
27    sql.push(')');
28    Clause::new(sql, params)
29}
30impl<'q> Clause<'q, Where> {
31    pub fn group_by_cols(self, columns: &[&str]) -> Clause<'q, Group> {
32        __group_by(self, cols(columns))
33    }
34    pub fn group_by<C>(self, group: Clause<'_, C>) -> Clause<'q, Group> {
35        __group_by(self, group)
36    }
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, Paging> {
46    pub fn limit(self, limit: &'q i64) -> Clause<'q, ()> {
47        __limit(self, limit)
48    }
49    pub fn offset(self, offset: &'q i64) -> Clause<'q, ()> {
50        __offset(self, offset)
51    }
52}
53impl<'q> Clause<'q, Group> {
54    pub fn order_by_cols(self, columns: &[&str]) -> Clause<'q, Order> {
55        __order_by_cols(self, columns)
56    }
57    pub fn limit(self, limit: &'q i64) -> Clause<'q, Paging> {
58        __limit(self, limit)
59    }
60    pub fn offset(self, offset: &'q i64) -> Clause<'q, Paging> {
61        __offset(self, offset)
62    }
63}
64
65impl<'q> Clause<'q, Order> {
66    pub fn limit(self, limit: &'q i64) -> Clause<'q, Paging> {
67        __limit(self, limit)
68    }
69    pub fn offset(self, offset: &'q i64) -> Clause<'q, Paging> {
70        __offset(self, offset)
71    }
72}
73
74impl<'q> Clause<'q, Select> {
75    /// Add more column in `SELECT` statement
76    ///
77    /// # Arguments
78    ///
79    /// * `clause`:
80    ///
81    /// returns: Clause<Select>
82    ///
83    /// # Examples
84    ///
85    /// ```
86    /// use ngb_sqlbuilder::select;
87    ///
88    /// .select(col("t1.ColName as Alias"))
89    /// ```
90    /// ```
91    ///  , t1."ColName" as "Alias"
92    /// ```
93    pub fn select<T>(self, clause: Clause<'q, T>) -> Clause<'q, Select> {
94        let mut sql = self.sql;
95        let mut params = self.params;
96        sql.push_str(", ");
97        sql.push_str(&clause.sql);
98        params.extend_from_slice(&clause.params);
99        Clause::new(sql, params)
100    }
101    pub fn from(self, table: &str) -> Clause<'q, From> {
102        let mut sql = self.sql;
103        sql.push_str(" FROM ");
104        // sql.push_str(&format_prim(table));
105        append_prim(&mut sql, table);
106        Clause::new(sql, self.params)
107    }
108    pub fn from_subquery<T>(self, sub: Clause<'q, T>, alias: &str) -> Clause<'q, From> {
109        let (mut sql, mut params) = self.unwrap();
110        let (sub_sql, sub_params) = sub.unwrap();
111        sql.push_str(" FROM (");
112        sql.push_str(&sub_sql);
113        params.extend_from_slice(&sub_params);
114        sql.push_str(") as ");
115        append_wrap(&mut sql, alias);
116        Clause::new(sql, params)
117    }
118}
119
120fn __join<'q, T>(
121    clause: Clause<'q, T>,
122    join: &str,
123    table: &str,
124    join_method: Clause<'q, Join>,
125) -> Clause<'q, From> {
126    let (mut sql, params) = clause.unwrap();
127    // sql.push(' ');
128    sql.push_str(&join);
129    sql.push_str(" JOIN ");
130    // sql.push_str(&format!(" {join}JOIN "));
131    // sql.push_str(&format_prim(table));
132    append_prim(&mut sql, table);
133    let (join_sql, _) = join_method.unwrap_ref();
134    sql.push_str(&join_sql);
135    Clause::new(sql, params)
136}
137impl<'q> Clause<'q, From> {
138    pub fn join(self, table: &str, join_method: Clause<'q, Join>) -> Clause<'q, From> {
139        __join(self, "", table, join_method)
140    }
141    pub fn left_join(self, table: &str, join_method: Clause<'q, Join>) -> Clause<'q, From> {
142        __join(self, " LEFT", table, join_method)
143    }
144
145    pub fn left_outer_join(self, table: &str, join_method: Clause<'q, Join>) -> Clause<'q, From> {
146        __join(self, " LEFT OUTER", table, join_method)
147    }
148
149    pub fn right_join(self, table: &str, join_method: Clause<'q, Join>) -> Clause<'q, From> {
150        __join(self, " RIGHT", table, join_method)
151    }
152
153    pub fn right_outer_join(self, table: &str, join_method: Clause<'q, Join>) -> Clause<'q, From> {
154        __join(self, " RIGHT OUTER", table, join_method)
155    }
156
157    pub fn where_col(self, name: &str, condition: Clause<'q, Condition>) -> Clause<'q, Where> {
158        __where(self, name, condition)
159    }
160    pub fn where_cond(self, condition: Clause<'q, Condition>) -> Clause<'q, Where> {
161        let (mut sql, params) = self.unwrap();
162        sql.push_str(" WHERE ");
163        __condition("", Clause::<From>::new(sql, params), condition)
164    }
165
166    pub fn limit(self, limit: &'q i64) -> Clause<'q, ()> {
167        __limit(self, limit)
168    }
169    pub fn offset(self, offset: &'q i64) -> Clause<'q, ()> {
170        __offset(self, offset)
171    }
172}
173fn __limit<'q, T, C>(clause: Clause<'q, T>, limit: &'q i64) -> Clause<'q, C> {
174    let (mut sql, mut params) = clause.unwrap();
175    sql.push_str(" LIMIT $");
176    params.push(limit);
177    Clause::new(sql, params)
178}
179fn __offset<'q, T, C>(clause: Clause<'q, T>, offset: &'q i64) -> Clause<'q, C> {
180    let (mut sql, mut params) = clause.unwrap();
181    sql.push_str(" OFFSET $");
182    params.push(offset);
183    Clause::new(sql, params)
184}
185
186impl<'q> Clause<'q, Join> {
187    // pub fn using(self, cols: Clause<Column>) -> Clause<'q, From> {
188    //     let (mut sql, params) = self.unwrap();
189    //     let (col_sql, _) = cols.unwrap();
190    //     sql.push_str(" using(");
191    //     sql.push_str(&col_sql);
192    //     sql.push(')');
193    //     Clause::new(sql, params)
194    // }
195    // pub fn using_col(self, col_name: &str) -> Clause<'q, From> {
196    //     let (mut sql, params) = self.unwrap();
197    //     sql.push_str(" using(");
198    //     sql.push_str(&format_col(col_name));
199    //     sql.push(')');
200    //     Clause::new(sql, params)
201    // }
202    // pub fn using_cols(self, col_names: &[&str]) -> Clause<'q, From> {
203    //     let (mut sql, params) = self.unwrap();
204    //     sql.push_str(" using(");
205    //     __cols(&mut sql, col_names);
206    //     sql.push(')');
207    //     Clause::new(sql, params)
208    // }
209    // pub fn on(self, col1: &str, col2: &str) -> Clause<'q, From> {
210    //     let (mut sql, params) = self.unwrap();
211    //     sql.push_str(" ON ");
212    //     sql.push_str(&format_col(col1));
213    //     sql.push_str(" = ");
214    //     sql.push_str(&format_col(col2));
215    //     Clause::new(sql, params)
216    // }
217    // pub fn on_wrap(self, condition: Clause<'q, Condition>) -> Clause<'q, From> {
218    //     let (mut sql, params) = self.unwrap();
219    //     let (cond_sql, _) = condition.unwrap();
220    //     sql.push_str(" ON (");
221    //     sql.push_str(&cond_sql);
222    //     sql.push(')');
223    //     Clause::new(sql, params)
224    // }
225}
226
227pub fn using<'q>(cols: Clause<Column>) -> Clause<'q, Join> {
228    // let (mut sql, params) = self.unwrap();
229    let mut sql = String::new();
230    let (col_sql, _) = cols.unwrap();
231    sql.push_str(" USING (");
232    sql.push_str(&col_sql);
233    sql.push(')');
234    Clause::new(sql, vec![])
235}
236///
237///
238/// # Arguments
239///
240/// * `col_name`: common column name
241///
242/// returns: `Clause<Join>`
243///
244/// # Examples
245///
246/// ```
247/// use ngb_sqlbuilder::using_col;
248/// using_col("Col1")
249/// // USING ("Col1")
250/// ```
251pub fn using_col<'q>(col_name: &str) -> Clause<'q, Join> {
252    // let (mut sql, params) = self.unwrap();
253    let mut sql = String::new();
254    sql.push_str(" USING (");
255    // sql.push_str(&format_col(col_name));
256    append_col(&mut sql, col_name);
257    sql.push(')');
258    Clause::new(sql, vec![])
259}
260///
261///
262/// # Arguments
263///
264/// * `col_names`: Array of column names
265///
266/// returns: `Clause<Join>`
267///
268/// # Examples
269///
270/// ```
271/// use ngb_sqlbuilder::using_col;
272///
273/// using_col(&["Col1", "Col2", ...])
274/// // USING ("Col1", "Col2", ...)
275/// ```
276pub fn using_cols<'q>(col_names: &[&str]) -> Clause<'q, Join> {
277    // let (mut sql, params) = self.unwrap();
278    let mut sql = String::new();
279    sql.push_str(" USING (");
280    __cols(&mut sql, col_names);
281    sql.push(')');
282    Clause::new(sql, vec![])
283}
284///
285///
286/// # Arguments
287///
288/// * `col1`:
289/// * `col2`:
290///
291/// returns: `Clause<Join>`
292///
293/// # Examples
294///
295/// ```
296/// use ngb_sqlbuilder::on_col_pair;
297///
298/// on_col_pair("t1.Col1","t2.Col1")
299/// // ON t1."Col1" = t2."Col1"
300/// ```
301pub fn on_col_pair<'q>(col1: &str, col2: &str) -> Clause<'q, Join> {
302    // let (mut sql, params) = self.unwrap();
303    let mut sql = String::new();
304    sql.push_str(" ON ");
305    // sql.push_str(&format_col(col1));
306    append_col(&mut sql, col1);
307    // sql.push_str(" = ");
308    sql.push('=');
309    // sql.push_str(&format_col(col2));
310    append_col(&mut sql, col2);
311    Clause::new(sql, vec![])
312}
313///
314///
315/// # Arguments
316///
317/// * `col_pairs`:
318///
319/// returns: `Clause<Join>`
320///
321/// # Examples
322///
323/// ```
324/// use ngb_sqlbuilder::on_col_pairs;
325///
326/// on_col_pairs(&[("t1.Col1","t2.Col1"),("t1.Col2","t2.Col2")])
327/// // ON (t1."Col1" = t2."Col1" AND t1."Col2" = t2."Col2")
328/// ```
329pub fn on_col_pairs<'q>(col_pairs: &[(&str, &str)]) -> Clause<'q, Join> {
330    let mut sql = String::new();
331    sql.push_str(" ON (");
332    let mut not_first = false;
333    for (col1, col2) in col_pairs {
334        if not_first {
335            sql.push_str(" AND ");
336        } else {
337            not_first = true;
338        }
339        sql.push_str(&format_col(col1));
340        // sql.push_str(" = ");
341        sql.push('=');
342        sql.push_str(&format_col(col2));
343    }
344    sql.push(')');
345    Clause::new(sql, vec![])
346}
347pub fn on_wrap(condition: Clause<Condition>) -> Clause<Join> {
348    // let (mut sql, params) = self.unwrap();
349    let mut sql = String::new();
350    let (cond_sql, _) = condition.unwrap();
351    sql.push_str(" ON (");
352    sql.push_str(&cond_sql);
353    sql.push(')');
354    Clause::new(sql, vec![])
355}
356
357pub(crate) fn __where<'q, T, C>(
358    clause: Clause<'q, T>,
359    name: &str,
360    condition: Clause<'q, Condition>,
361) -> Clause<'q, C> {
362    let mut sql = clause.sql;
363    let mut params = clause.params;
364    sql.push_str(" WHERE ");
365    sql.push_str(&format_col(name));
366    let (cond_sql, cond_params) = condition.unwrap();
367    sql.push_str(cond_sql.as_str());
368    params.extend(cond_params);
369    Clause::new(sql, params)
370}
371
372impl<'q> Clause<'q, Where> {
373    pub fn and(self, name: &str, condition: Clause<'q, Condition>) -> Clause<'q, Where> {
374        __and_col(self, name, condition)
375    }
376    pub fn or(self, name: &str, condition: Clause<'q, Condition>) -> Clause<'q, Where> {
377        __or_col(self, name, condition)
378    }
379
380    pub fn and_nest(self, clause: Clause<'q, Condition>) -> Clause<'q, Where> {
381        __and_nest(self, clause)
382    }
383
384    pub fn or_nest(self, clause: Clause<'q, Condition>) -> Clause<'q, Where> {
385        __or_nest(self, clause)
386    }
387
388    pub fn order_by_cols(self, columns: &[&str]) -> Clause<'q, Order> {
389        __order_by_cols(self, columns)
390        // let (mut sql, params) = self.unwrap();
391        // let mut not_first = false;
392        // sql.push_str(" order by ");
393        // for col in columns {
394        //     if not_first {
395        //         sql.push_str(", ");
396        //     } else {
397        //         not_first = true;
398        //     }
399        //     sql.push_str(&__order(col));
400        // }
401        // Clause::new(sql, params)
402    }
403}
404fn __order_by_cols<'q, T>(clause: Clause<'q, T>, columns: &[&str]) -> Clause<'q, Order> {
405    let (mut sql, params) = clause.unwrap();
406    let mut not_first = false;
407    sql.push_str(" ORDER BY ");
408    for col in columns {
409        if not_first {
410            sql.push_str(", ");
411        } else {
412            not_first = true;
413        }
414        sql.push_str(&__order(col));
415    }
416    Clause::new(sql, params)
417}
418fn __order(col: &str) -> String {
419    let (col_name, dir) = {
420        let mut split_as = col.split(" ");
421        let col_name = split_as.next().unwrap_or_default();
422        let alias = split_as.next();
423        (wrap(col_name), alias)
424    };
425    if let Some(dir) = dir
426        && dir.eq_ignore_ascii_case("desc")
427    {
428        format!("{} {}", col_name, dir)
429    } else {
430        format!("{}", col_name)
431    }
432}
433
434// pub trait FromClause {}
435
436/// Begin `SELECT` statement
437///
438/// # Arguments
439///
440/// * `clause`:
441///
442/// returns: `Clause<Select>`
443///
444/// # Examples
445///
446/// ```
447/// use ngb_sqlbuilder::{cols, select};
448///
449/// select(cols(&["t.Id","t.Name","t.Value"]))
450/// // SQL: SELECT t."Id", t."Name", t."Value"
451/// ```
452pub fn select(clause: Clause<Column>) -> Clause<Select> {
453    Clause::new(format!("SELECT {}", clause.sql), clause.params)
454}
455
456pub fn select_all<'q>() -> Clause<'q, Select> {
457    Clause::new("SELECT *".to_string(), Vec::new())
458}
459
460/// Create column clause
461///
462/// # Arguments
463///
464/// * `expr`: column expression
465///
466/// returns: `Clause<Column>`
467///
468/// # Examples
469///
470/// ```
471/// use ngb_sqlbuilder::{col, select};
472///
473/// // Simple name
474/// select(col("col_name"))
475/// // SQL: SELECT "col_name"
476///
477/// // With table source
478/// select(col("t.ColName"))
479/// // SQL: SELECT t."ColName"
480///
481/// // With cast
482/// select(col("ColName::text"))
483/// // SQL: SELECT "ColName"::text
484///
485/// // With alias
486/// select(col("ColName as Alias"))
487/// // SQL: SELECT "ColName" as "Alias"
488///
489/// // Complete
490/// select(col("t.ColName::text as Alias"))
491/// // SQL: SELECT t."ColName"::text as "Alias"
492/// ```
493pub fn col<'q>(expr: &str) -> Clause<'q, Column> {
494    Clause::new(format_prim(expr), vec![])
495}
496
497/// Add value bind
498///
499/// # Arguments
500///
501/// * `value`: value, will be parameterized to `$n`
502///
503/// returns: `Clause<S>`
504///
505/// # Examples
506///
507/// ```
508/// use ngb_sqlbuilder::{select, val};
509///
510/// select(val(&123))
511/// // SQL: SELECT $1
512/// // Param: [123]
513/// ```
514pub fn val<'q, T: ToSql + Sync + 'q, S>(value: &'q T) -> Clause<'q, S> {
515    Clause::new("$".to_string(), vec![value])
516}
517pub fn where_col<'q>(name: &str, condition: Clause<'q, Condition>) -> Clause<'q, Where> {
518    let mut sql = String::new();
519    let (cond_sql, cond_params) = condition.unwrap();
520    sql.push(' ');
521    sql.push_str(&format_col(name));
522    sql.push_str(cond_sql.as_str());
523    Clause::new(sql, cond_params)
524}
525
526#[cfg(test)]
527mod tests {
528    use super::*;
529
530    #[test]
531    fn example() {
532        let user_id: i32 = 123;
533        let limit: i64 = 10;
534        let offset: i64 = 50;
535        let (sql, params) = select(cols(&[
536            "u.UserId as id",
537            "u.Name as name",
538            "p.Title as title",
539            "p.Content::text as body",
540        ]))
541        .from("User as u")
542        .left_join("Post as p", using_col("UserId"))
543        .where_col("u.UserId", eq(&user_id).cast("bigint"))
544        .order_by_cols(&["CreatedAt desc"])
545        .limit(&limit)
546        .offset(&offset)
547        .build();
548
549        println!("SQL: {}", sql);
550        println!("Params: {:?}", params);
551        // let rows = client.query(&sql, &params).await.unwrap();
552    }
553    #[test]
554    fn select_test() {
555        let user_id: i32 = 123;
556        let (sql, params) = select(cols(&["u.UserId as Id", "u.Name", "p.Title", "p.Content"]))
557            .from("User as u")
558            .left_join("Post as p", using_col("UserId"))
559            .left_join("Metadata as m", on_col_pair("p.Id", "m.PostId"))
560            .left_join(
561                "History as h",
562                on_col_pairs(&[("p.Id", "m.PostId"), ("p.Id", "h.PostId")]),
563            )
564            .where_col("u.UserId", eq(&user_id))
565            .and("t.Value", lt(&1000))
566            .and_nest(col("t.Asd").eq(&true).or_col("Xwe").eq(&false))
567            .order_by_cols(&["CreatedAt desc"])
568            .limit(&10)
569            .offset(&100)
570            .build();
571        // let column = col("a.foo as dfg");
572        println!("{}", sql);
573        println!("{:?}", params);
574    }
575
576    #[test]
577    fn nested() {
578        let q = select(cols(&["u.UserId", "u.Name"]))
579            .select(avg_col("t.Score").alias("AvgScore"))
580            .select(avg_col_f64("t.Rating").alias("AvgRating"))
581            .select(extract_col("epoch", "Timestamp").cast("int").alias("LogId"))
582            .from("User as u")
583            .where_cond(ands(&[
584                col("u.UserId").eq(&123),
585                ors(&[col("u.UserId").eq(&456), col_op("u.Id", ">=", &789)]),
586            ]))
587            .build();
588        println!("{}", q.0);
589    }
590
591    #[test]
592    fn branched() {
593        let opt1 = Some(1);
594        let mut query = select(cols(&["u.UserId", "u.Name", "p.Title", "p.Content"]))
595            .from("User as u")
596            .where_col("u.UserId", eq(&opt1))
597            .debug();
598        println!("Display: {}", query);
599        println!("Debug: {:?}", query);
600        if let Some(val) = &opt1 {
601            query = query.and("u.PostId", eq(val));
602        }
603        query.and("u.Title", eq(&1));
604    }
605
606    #[test]
607    fn subquery() {
608        let q = select(cols(&["sub.UserId", "sub.Name"]))
609            .from_subquery(
610                select(cols(&["u.UserId", "u.Name"])).from("Table as u"),
611                "sub",
612            )
613            .where_col("u.UserId", eq(&123));
614        println!("Display: {}", q);
615        println!("Debug: {:?}", q);
616    }
617
618    #[test]
619    fn union() {
620        let opt1 = Some(1);
621        let query = select(cols(&["u.UserId", "u.Name", "p.Title", "p.Content"]))
622            .from("User as u")
623            .where_col("u.UserId", eq(&opt1))
624            .union(
625                select(cols(&["u.UserId", "u.Name", "p.Title", "p.Content"]))
626                    .from("User as u")
627                    .where_col("u.UserId", eq(&opt1)),
628            )
629            .union_all(
630                select(cols(&["u.UserId", "u.Name", "p.Title", "p.Content"]))
631                    .from("User as u")
632                    .where_col("u.UserId", eq(&opt1)),
633            );
634        println!("Display: {}", query);
635        println!("Debug: {:?}", query);
636    }
637}