1use crate::{Clause, Column, SqlClause, wrap};
2use tokio_postgres::types::ToSql;
3
4pub struct Insert;
5pub struct End;
6pub struct OnConflictHandle<'q>(Clause<'q, End>);
7
8
9pub fn insert_into<'q>(
25 table: &str,
26 map: &[(&str, &'q (dyn ToSql + Sync + 'q))],
27) -> Clause<'q, Insert> {
28 let mut sql = String::from("INSERT INTO ");
29 let mut value_list = Vec::with_capacity(map.len());
30 sql.push_str(&wrap(table));
31 sql.push_str(" (");
32 let mut not_first = false;
33 for (key, value) in map {
34 if not_first {
35 sql.push(',');
36 }
37 sql.push_str(format!("\"{}\"", key).as_str());
38 not_first = true;
39 value_list.push(*value);
40 }
41 sql.push_str(") VALUES (");
42 let mut not_first = false;
43 for _ in 0..value_list.len() {
44 if not_first {
45 sql.push(',');
46 }
47 not_first = true;
48 sql.push('$');
49 }
50 sql.push(')');
51 Clause::new(sql, value_list)
52}
53impl<'q> Clause<'q, Insert> {
54 pub fn on_conflict_do_nothing(self) -> Clause<'q, ()> {
55 let (mut sql, params) = self.unwrap();
56 sql.push_str(" ON CONFLICT DO NOTHING");
57 Clause::new(sql, params)
58 }
59 pub fn on_conflict(self, cols: Clause<'q, Column>) -> OnConflictHandle<'q> {
60 let (mut sql, params) = self.unwrap();
61 let (cols_sql, _) = cols.unwrap();
62 sql.push_str(" ON CONFLICT (");
63 sql.push_str(&cols_sql);
64 sql.push(')');
65 OnConflictHandle(Clause::new(sql, params))
66 }
67}
68
69impl<'q> OnConflictHandle<'q> {
70 pub fn do_nothing(self) -> Clause<'q, ()> {
71 let (mut sql, params) = self.0.unwrap();
72 sql.push_str(" DO NOTHING");
73 Clause::new(sql, params)
74 }
75
76 pub fn do_update_excluded(self, columns: &[&str]) -> Clause<'q, ()> {
83 let (mut sql, params) = self.0.unwrap();
84 sql.push_str(" DO UPDATE SET ");
85 let mut not_first = false;
86 for item in columns {
87 if not_first {
88 sql.push(',');
89 }
90 not_first = true;
91 sql.push_str(&format!("\"{}\" = excluded.\"{}\"", item, item));
92 }
93 Clause::new(sql, params)
94 }
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100 use crate::{cols, Build};
101
102 #[test]
103 fn insert_test1() {
104 let title = String::from("title");
105 let content = String::from("content");
106 let image_id = 123;
107 let (sql, params) = insert_into(
108 "Post",
109 &[
110 ("Title", &title),
111 ("Content", &content),
112 ("ImageId", &image_id),
113 ],
114 )
115 .on_conflict(cols(&["Title"]))
116 .do_update_excluded(&["Content", "ImageId"])
117 .build();
118 println!("{:}", sql);
119 assert_eq!(
120 sql,
121 r##"INSERT INTO "Post" ("Title","Content","ImageId") VALUES ($1,$2,$3) ON CONFLICT ("Title") DO UPDATE SET "Content" = excluded."Content","ImageId" = excluded."ImageId""##
122 );
123 println!("params: {:?}", params);
124 }
125 #[test]
126 fn insert_test2() {
127 let title = String::from("title");
128 let content = String::from("content");
129 let image_id = 123;
130 let (sql, params) = insert_into(
131 "Post",
132 &[
133 ("Title", &title),
134 ("Content", &content),
135 ("ImageId", &image_id),
136 ],
137 )
138 .on_conflict(cols(&["Title"]))
139 .do_update_excluded(&["Content", "ImageId"])
140 .combine_raw(r##"returning "Id""##, &[])
141 .build();
142 println!("{:}", sql);
143 println!("params: {:?}", params);
144 }
145}