Skip to main content

ngb_sqlbuilder/
insert.rs

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