1use std::fmt::Write;
2
3#[cfg(feature = "mysql")]
4use crate::db_specific::mysql;
5#[cfg(feature = "postgres")]
6use crate::db_specific::postgres;
7#[cfg(feature = "sqlite")]
8use crate::db_specific::sqlite;
9use crate::on_conflict::OnConflict;
10use crate::value::NullType;
11use crate::Value;
12
13pub trait Insert<'post_build> {
17 fn rollback_transaction(self) -> Self;
27
28 fn build(self) -> (String, Vec<Value<'post_build>>);
33}
34
35#[derive(Debug)]
39pub struct InsertData<'until_build, 'post_build> {
40 pub(crate) into_clause: &'until_build str,
41 pub(crate) columns: &'until_build [&'until_build str],
42 pub(crate) row_values: &'until_build [&'until_build [Value<'post_build>]],
43 pub(crate) lookup: Vec<Value<'post_build>>,
44 pub(crate) on_conflict: OnConflict,
45 pub(crate) returning_clause: Option<&'until_build [&'until_build str]>,
46}
47
48#[derive(Debug)]
54pub enum InsertImpl<'until_build, 'post_build> {
55 #[cfg(feature = "sqlite")]
59 SQLite(InsertData<'until_build, 'post_build>),
60 #[cfg(feature = "mysql")]
64 MySQL(InsertData<'until_build, 'post_build>),
65 #[cfg(feature = "postgres")]
69 Postgres(InsertData<'until_build, 'post_build>),
70}
71
72impl<'post_build> Insert<'post_build> for InsertImpl<'_, 'post_build> {
73 fn rollback_transaction(mut self) -> Self {
74 match self {
75 #[cfg(feature = "sqlite")]
76 InsertImpl::SQLite(ref mut d) => d.on_conflict = OnConflict::ROLLBACK,
77 #[cfg(feature = "mysql")]
78 InsertImpl::MySQL(ref mut d) => d.on_conflict = OnConflict::ROLLBACK,
79 #[cfg(feature = "postgres")]
80 InsertImpl::Postgres(ref mut d) => d.on_conflict = OnConflict::ROLLBACK,
81 };
82 self
83 }
84
85 fn build(self) -> (String, Vec<Value<'post_build>>) {
86 match self {
87 #[cfg(feature = "sqlite")]
88 InsertImpl::SQLite(mut d) => {
89 if d.columns.is_empty() {
91 let mut s = format!(
92 "INSERT {}INTO \"{}\" DEFAULT VALUES",
93 match d.on_conflict {
94 OnConflict::ABORT => "OR ABORT ",
95 OnConflict::ROLLBACK => "OR ROLLBACK ",
96 },
97 d.into_clause,
98 );
99
100 if let Some(ret_clause) = d.returning_clause {
101 write!(s, " RETURNING ").unwrap();
102
103 for (idx, c) in ret_clause.iter().enumerate() {
104 write!(s, "\"{c}\"").unwrap();
105
106 if idx != ret_clause.len() - 1 {
107 write!(s, ", ").unwrap();
108 }
109 }
110 }
111 write!(s, ";").unwrap();
112
113 return (s, d.lookup);
114 }
115
116 let mut s = format!(
117 "INSERT {}INTO \"{}\" (",
118 match d.on_conflict {
119 OnConflict::ABORT => "OR ABORT ",
120 OnConflict::ROLLBACK => "OR ROLLBACK ",
121 },
122 d.into_clause,
123 );
124 for (idx, x) in d.columns.iter().enumerate() {
125 write!(s, "\"{x}\"").unwrap();
126 if idx != d.columns.len() - 1 {
127 write!(s, ", ").unwrap();
128 }
129 }
130 write!(s, ") VALUES ").unwrap();
131
132 for (idx, x) in d.row_values.iter().enumerate() {
133 write!(s, "(").unwrap();
134 for (idx_2, y) in x.iter().enumerate() {
135 match y {
136 Value::Ident(st) => write!(s, "\"{}\"", *st).unwrap(),
137 Value::Choice(c) => write!(s, "{}", sqlite::fmt(c)).unwrap(),
138 Value::Null(NullType::Choice) => write!(s, "NULL").unwrap(),
139 _ => {
140 d.lookup.push(*y);
141 write!(s, "?").unwrap();
142 }
143 }
144 if idx_2 != x.len() - 1 {
145 write!(s, ", ").unwrap();
146 }
147 }
148 write!(s, ")").unwrap();
149 if idx != d.row_values.len() - 1 {
150 write!(s, ", ").unwrap();
151 }
152 }
153
154 if let Some(ret_clause) = d.returning_clause {
155 write!(s, " RETURNING ").unwrap();
156
157 for (idx, c) in ret_clause.iter().enumerate() {
158 write!(s, "\"{c}\"").unwrap();
159
160 if idx != ret_clause.len() - 1 {
161 write!(s, ", ").unwrap();
162 }
163 }
164 }
165
166 write!(s, ";").unwrap();
167
168 (s, d.lookup)
169 }
170 #[cfg(feature = "mysql")]
171 InsertImpl::MySQL(mut d) => {
172 if d.columns.is_empty() {
173 let mut s = format!(
174 "INSERT {}INTO `{}` DEFAULT VALUES",
175 match d.on_conflict {
176 OnConflict::ABORT => "OR ABORT ",
177 OnConflict::ROLLBACK => "OR ROLLBACK ",
178 },
179 d.into_clause,
180 );
181
182 if let Some(ret_clause) = d.returning_clause {
183 write!(s, " RETURNING ").unwrap();
184
185 for (idx, c) in ret_clause.iter().enumerate() {
186 write!(s, "`{c}`").unwrap();
187
188 if idx != ret_clause.len() - 1 {
189 write!(s, ", ").unwrap();
190 }
191 }
192 }
193 write!(s, ";").unwrap();
194
195 return (s, d.lookup);
196 }
197
198 let mut s = format!("INSERT INTO `{}` (", d.into_clause);
199 for (idx, x) in d.columns.iter().enumerate() {
200 write!(s, "`{x}`").unwrap();
201 if idx != d.columns.len() - 1 {
202 write!(s, ", ").unwrap();
203 }
204 }
205 write!(s, ") VALUES ").unwrap();
206
207 for (idx, x) in d.row_values.iter().enumerate() {
208 write!(s, "(").unwrap();
209 for (idx_2, y) in x.iter().enumerate() {
210 match y {
211 Value::Ident(st) => write!(s, "{}", *st).unwrap(),
212 Value::Choice(c) => write!(s, "{}", mysql::fmt(c)).unwrap(),
213 Value::Null(NullType::Choice) => write!(s, "NULL").unwrap(),
214 _ => {
215 d.lookup.push(*y);
216 write!(s, "?").unwrap();
217 }
218 }
219 if idx_2 != x.len() - 1 {
220 write!(s, ", ").unwrap();
221 }
222 }
223 write!(s, ")").unwrap();
224 if idx != d.row_values.len() - 1 {
225 write!(s, ", ").unwrap();
226 }
227 }
228
229 if let Some(ret_clause) = d.returning_clause {
230 write!(s, " RETURNING ").unwrap();
231
232 for (idx, c) in ret_clause.iter().enumerate() {
233 write!(s, "`{c}`").unwrap();
234
235 if idx != ret_clause.len() - 1 {
236 write!(s, ", ").unwrap();
237 }
238 }
239 }
240
241 write!(s, ";").unwrap();
242
243 (s, d.lookup)
244 }
245 #[cfg(feature = "postgres")]
246 InsertImpl::Postgres(mut d) => {
247 if d.columns.is_empty() {
248 let mut s = format!("INSERT INTO \"{}\" DEFAULT VALUES", d.into_clause);
249
250 if let Some(ret_clause) = d.returning_clause {
251 write!(s, " RETURNING ").unwrap();
252
253 for (idx, c) in ret_clause.iter().enumerate() {
254 write!(s, "\"{c}\"").unwrap();
255
256 if idx != ret_clause.len() - 1 {
257 write!(s, ", ").unwrap();
258 }
259 }
260 }
261 write!(s, ";").unwrap();
262
263 return (s, d.lookup);
264 }
265
266 let mut s = format!("INSERT INTO \"{}\" (", d.into_clause);
267 for (idx, x) in d.columns.iter().enumerate() {
268 write!(s, "\"{x}\"").unwrap();
269 if idx != d.columns.len() - 1 {
270 write!(s, ", ").unwrap();
271 }
272 }
273 write!(s, ") VALUES ").unwrap();
274
275 for (idx, x) in d.row_values.iter().enumerate() {
276 write!(s, "(").unwrap();
277 for (idx_2, y) in x.iter().enumerate() {
278 match y {
279 Value::Ident(st) => write!(s, "\"{}\"", *st).unwrap(),
280 Value::Choice(c) => write!(s, "{}", postgres::fmt(c)).unwrap(),
281 Value::Null(NullType::Choice) => write!(s, "NULL").unwrap(),
282 _ => {
283 d.lookup.push(*y);
284 write!(s, "${}", d.lookup.len()).unwrap();
285 }
286 }
287 if idx_2 != x.len() - 1 {
288 write!(s, ", ").unwrap();
289 }
290 }
291 write!(s, ")").unwrap();
292 if idx != d.row_values.len() - 1 {
293 write!(s, ", ").unwrap();
294 }
295 }
296
297 if let Some(ret_clause) = d.returning_clause {
298 write!(s, " RETURNING ").unwrap();
299
300 for (idx, c) in ret_clause.iter().enumerate() {
301 write!(s, "\"{c}\"").unwrap();
302
303 if idx != ret_clause.len() - 1 {
304 write!(s, ", ").unwrap();
305 }
306 }
307 }
308
309 write!(s, ";").unwrap();
310
311 (s, d.lookup)
312 }
313 }
314 }
315}