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_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}