rorm_sql/
insert.rs

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
13/**
14Trait representing a insert builder.
15 */
16pub trait Insert<'post_build> {
17    /**
18    Turns on ROLLBACK mode.
19
20    Only useful in case of an active transaction.
21
22    If the insert fails, the complete transaction will be rolled back.
23    The default case is to just stop the transaction, but not rollback any
24    prior successful executed queries.
25     */
26    fn rollback_transaction(self) -> Self;
27
28    /**
29    This method is used to build the INSERT query.
30    It returns the build query as well as a vector of values to bind to it.
31     */
32    fn build(self) -> (String, Vec<Value<'post_build>>);
33}
34
35/**
36Representation of the data of a INSERT operation in SQL.
37*/
38#[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/**
49Implementation of the [Insert] trait for the different implementations.
50
51Should only be constructed via [DBImpl::insert](crate::DBImpl::insert).
52 */
53#[derive(Debug)]
54pub enum InsertImpl<'until_build, 'post_build> {
55    /**
56    SQLite representation of the INSERT operation.
57     */
58    #[cfg(feature = "sqlite")]
59    SQLite(InsertData<'until_build, 'post_build>),
60    /**
61    MySQL representation of the INSERT operation.
62     */
63    #[cfg(feature = "mysql")]
64    MySQL(InsertData<'until_build, 'post_build>),
65    /**
66    Postgres representation of the INSERT operation.
67     */
68    #[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                // Handle case, if no columns should be inserted, aka an empty insert
90                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}