Skip to main content

rorm_sql/
insert.rs

1use std::fmt::Write;
2
3#[cfg(feature = "postgres")]
4use crate::db_specific::postgres;
5#[cfg(feature = "sqlite")]
6use crate::db_specific::sqlite;
7use crate::on_conflict::OnConflict;
8use crate::value::NullType;
9use crate::Value;
10
11/**
12Trait representing a insert builder.
13 */
14pub trait Insert<'post_build> {
15    /**
16    Turns on ROLLBACK mode.
17
18    Only useful in case of an active transaction.
19
20    If the insert fails, the complete transaction will be rolled back.
21    The default case is to just stop the transaction, but not rollback any
22    prior successful executed queries.
23     */
24    fn rollback_transaction(self) -> Self;
25
26    /**
27    This method is used to build the INSERT query.
28    It returns the build query as well as a vector of values to bind to it.
29     */
30    fn build(self) -> (String, Vec<Value<'post_build>>);
31}
32
33/**
34Representation of the data of a INSERT operation in SQL.
35*/
36#[derive(Debug)]
37pub struct InsertData<'until_build, 'post_build> {
38    pub(crate) into_clause: &'until_build str,
39    pub(crate) columns: &'until_build [&'until_build str],
40    pub(crate) row_values: &'until_build [&'until_build [Value<'post_build>]],
41    pub(crate) lookup: Vec<Value<'post_build>>,
42    pub(crate) on_conflict: OnConflict,
43    pub(crate) returning_clause: Option<&'until_build [&'until_build str]>,
44}
45
46/**
47Implementation of the [Insert] trait for the different implementations.
48
49Should only be constructed via [DBImpl::insert](crate::DBImpl::insert).
50 */
51#[derive(Debug)]
52pub enum InsertImpl<'until_build, 'post_build> {
53    /**
54    SQLite representation of the INSERT operation.
55     */
56    #[cfg(feature = "sqlite")]
57    SQLite(InsertData<'until_build, 'post_build>),
58    /**
59    Postgres representation of the INSERT operation.
60     */
61    #[cfg(feature = "postgres")]
62    Postgres(InsertData<'until_build, 'post_build>),
63}
64
65impl<'post_build> Insert<'post_build> for InsertImpl<'_, 'post_build> {
66    fn rollback_transaction(mut self) -> Self {
67        match self {
68            #[cfg(feature = "sqlite")]
69            InsertImpl::SQLite(ref mut d) => d.on_conflict = OnConflict::ROLLBACK,
70            #[cfg(feature = "postgres")]
71            InsertImpl::Postgres(ref mut d) => d.on_conflict = OnConflict::ROLLBACK,
72        };
73        self
74    }
75
76    fn build(self) -> (String, Vec<Value<'post_build>>) {
77        match self {
78            #[cfg(feature = "sqlite")]
79            InsertImpl::SQLite(mut d) => {
80                // Handle case, if no columns should be inserted, aka an empty insert
81                if d.columns.is_empty() {
82                    let mut s = format!(
83                        "INSERT {}INTO \"{}\" DEFAULT VALUES",
84                        match d.on_conflict {
85                            OnConflict::ABORT => "OR ABORT ",
86                            OnConflict::ROLLBACK => "OR ROLLBACK ",
87                        },
88                        d.into_clause,
89                    );
90
91                    if let Some(ret_clause) = d.returning_clause {
92                        write!(s, " RETURNING ").unwrap();
93
94                        for (idx, c) in ret_clause.iter().enumerate() {
95                            write!(s, "\"{c}\"").unwrap();
96
97                            if idx != ret_clause.len() - 1 {
98                                write!(s, ", ").unwrap();
99                            }
100                        }
101                    }
102                    write!(s, ";").unwrap();
103
104                    return (s, d.lookup);
105                }
106
107                let mut s = format!(
108                    "INSERT {}INTO \"{}\" (",
109                    match d.on_conflict {
110                        OnConflict::ABORT => "OR ABORT ",
111                        OnConflict::ROLLBACK => "OR ROLLBACK ",
112                    },
113                    d.into_clause,
114                );
115                for (idx, x) in d.columns.iter().enumerate() {
116                    write!(s, "\"{x}\"").unwrap();
117                    if idx != d.columns.len() - 1 {
118                        write!(s, ", ").unwrap();
119                    }
120                }
121                write!(s, ") VALUES ").unwrap();
122
123                for (idx, x) in d.row_values.iter().enumerate() {
124                    write!(s, "(").unwrap();
125                    for (idx_2, y) in x.iter().enumerate() {
126                        match y {
127                            #[allow(deprecated)]
128                            Value::Ident(st) => write!(s, "\"{}\"", *st).unwrap(),
129                            Value::Choice(c) => write!(s, "{}", sqlite::fmt(c)).unwrap(),
130                            Value::Null(NullType::Choice) => write!(s, "NULL").unwrap(),
131                            _ => {
132                                d.lookup.push(*y);
133                                write!(s, "?").unwrap();
134                            }
135                        }
136                        if idx_2 != x.len() - 1 {
137                            write!(s, ", ").unwrap();
138                        }
139                    }
140                    write!(s, ")").unwrap();
141                    if idx != d.row_values.len() - 1 {
142                        write!(s, ", ").unwrap();
143                    }
144                }
145
146                if let Some(ret_clause) = d.returning_clause {
147                    write!(s, " RETURNING ").unwrap();
148
149                    for (idx, c) in ret_clause.iter().enumerate() {
150                        write!(s, "\"{c}\"").unwrap();
151
152                        if idx != ret_clause.len() - 1 {
153                            write!(s, ", ").unwrap();
154                        }
155                    }
156                }
157
158                write!(s, ";").unwrap();
159
160                (s, d.lookup)
161            }
162            #[cfg(feature = "postgres")]
163            InsertImpl::Postgres(mut d) => {
164                if d.columns.is_empty() {
165                    let mut s = format!("INSERT INTO \"{}\" DEFAULT VALUES", d.into_clause);
166
167                    if let Some(ret_clause) = d.returning_clause {
168                        write!(s, " RETURNING ").unwrap();
169
170                        for (idx, c) in ret_clause.iter().enumerate() {
171                            write!(s, "\"{c}\"").unwrap();
172
173                            if idx != ret_clause.len() - 1 {
174                                write!(s, ", ").unwrap();
175                            }
176                        }
177                    }
178                    write!(s, ";").unwrap();
179
180                    return (s, d.lookup);
181                }
182
183                let mut s = format!("INSERT INTO \"{}\" (", d.into_clause);
184                for (idx, x) in d.columns.iter().enumerate() {
185                    write!(s, "\"{x}\"").unwrap();
186                    if idx != d.columns.len() - 1 {
187                        write!(s, ", ").unwrap();
188                    }
189                }
190                write!(s, ") VALUES ").unwrap();
191
192                for (idx, x) in d.row_values.iter().enumerate() {
193                    write!(s, "(").unwrap();
194                    for (idx_2, y) in x.iter().enumerate() {
195                        match y {
196                            #[allow(deprecated)]
197                            Value::Ident(st) => write!(s, "\"{}\"", *st).unwrap(),
198                            Value::Choice(c) => write!(s, "{}", postgres::fmt(c)).unwrap(),
199                            Value::Null(NullType::Choice) => write!(s, "NULL").unwrap(),
200                            _ => {
201                                d.lookup.push(*y);
202                                write!(s, "${}", d.lookup.len()).unwrap();
203                            }
204                        }
205                        if idx_2 != x.len() - 1 {
206                            write!(s, ", ").unwrap();
207                        }
208                    }
209                    write!(s, ")").unwrap();
210                    if idx != d.row_values.len() - 1 {
211                        write!(s, ", ").unwrap();
212                    }
213                }
214
215                if let Some(ret_clause) = d.returning_clause {
216                    write!(s, " RETURNING ").unwrap();
217
218                    for (idx, c) in ret_clause.iter().enumerate() {
219                        write!(s, "\"{c}\"").unwrap();
220
221                        if idx != ret_clause.len() - 1 {
222                            write!(s, ", ").unwrap();
223                        }
224                    }
225                }
226
227                write!(s, ";").unwrap();
228
229                (s, d.lookup)
230            }
231        }
232    }
233}