Skip to main content

ngb_sqlbuilder/
insert.rs

1use crate::{Column, SqlClause, wrap, Clause};
2use tokio_postgres::types::ToSql;
3
4pub struct Insert;
5pub struct End;
6pub struct OnConflictHandle<'q>(Clause<'q, End>);
7
8pub fn insert_into<'q>(
9    table: &str,
10    map: &[(&str, &'q (dyn ToSql + Sync + 'q))],
11) -> Clause<'q, Insert> {
12    let mut sql = String::from("INSERT INTO ");
13    let mut value_list = Vec::with_capacity(map.len());
14    sql.push_str(&wrap(table));
15    sql.push_str(" (");
16    let mut not_first = false;
17    for (key, value) in map {
18        if not_first {
19            sql.push(',');
20        }
21        sql.push_str(format!("\"{}\"", key).as_str());
22        not_first = true;
23        value_list.push(*value);
24    }
25    sql.push_str(") VALUES (");
26    let mut not_first = false;
27    for _ in 0..value_list.len() {
28        if not_first {
29            sql.push(',');
30        }
31        not_first = true;
32        sql.push('$');
33    }
34    sql.push(')');
35    Clause::new(sql, value_list)
36}
37impl<'q> Clause<'q, Insert> {
38    pub fn on_conflict_do_nothing(self) -> Clause<'q, ()> {
39        let (mut sql, params) = self.unwrap();
40        sql.push_str(" ON CONFLICT DO NOTHING");
41        Clause::new(sql, params)
42    }
43    pub fn on_conflict(self, cols: Clause<'q, Column>) -> OnConflictHandle<'q> {
44        let (mut sql, params) = self.unwrap();
45        let (cols_sql, _) = cols.unwrap();
46        sql.push_str(" ON CONFLICT (");
47        sql.push_str(&cols_sql);
48        sql.push(')');
49        OnConflictHandle(Clause::new(sql, params))
50    }
51}
52
53impl<'q> OnConflictHandle<'q> {
54    pub fn do_nothing(self) -> Clause<'q, ()> {
55        let (mut sql, params) = self.0.unwrap();
56        sql.push_str(" DO NOTHING");
57        Clause::new(sql, params)
58    }
59
60    // pub fn do_update(self, column: Clause<'q, Column>) -> Clause<'q, Final> {
61    //     let (mut sql, params) = self.0.build();
62    //     sql.push_str(" do update ");
63    //     Clause::new(sql, params)
64    // }
65
66    pub fn do_update_excluded(self, columns: &[&str]) -> Clause<'q, ()> {
67        let (mut sql, params) = self.0.unwrap();
68        sql.push_str(" DO UPDATE SET ");
69        let mut not_first = false;
70        for item in columns {
71            if not_first {
72                sql.push(',');
73            }
74            not_first = true;
75            sql.push_str(&format!("\"{}\" = excluded.\"{}\"", item, item));
76        }
77        Clause::new(sql, params)
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84    use crate::cols;
85
86    #[test]
87    fn insert_test() {
88        let title = String::from("title");
89        let content = String::from("content");
90        let image_id = 123;
91        let (sql, params) = insert_into(
92            "Post",
93            &[
94                ("Title", &title),
95                ("Content", &content),
96                ("ImageId", &image_id),
97            ],
98        )
99        .on_conflict(cols(&["Title"]))
100        .do_update_excluded(&["Content", "ImageId"])
101        .build();
102        println!("{:}", sql);
103        assert_eq!(
104            sql,
105            r##"INSERT INTO "Post" ("Title","Content","ImageId") VALUES ($1,$2,$3) ON CONFLICT ("Title") DO UPDATE SET "Content" = excluded."Content","ImageId" = excluded."ImageId""##
106        );
107        println!("params: {:?}", params);
108    }
109}