grafbase_sql_ast/ast/
insert.rs

1use std::borrow::Cow;
2
3use anyhow::anyhow;
4
5use crate::ast::{Column, Expression, Query, Row, Table, Update, Values};
6
7/// A builder for an `INSERT` statement.
8#[derive(Clone, Debug, PartialEq)]
9pub struct Insert<'a> {
10    pub(crate) table: Option<Table<'a>>,
11    pub(crate) columns: Vec<Column<'a>>,
12    pub(crate) values: Expression<'a>,
13    pub(crate) on_conflict: Option<OnConflict<'a>>,
14    pub(crate) returning: Option<Vec<Column<'a>>>,
15    pub(crate) comment: Option<Cow<'a, str>>,
16}
17
18/// A builder for an `INSERT` statement for a single row.
19#[derive(Clone, Debug, PartialEq)]
20pub struct SingleRowInsert<'a> {
21    pub(crate) table: Option<Table<'a>>,
22    pub(crate) columns: Vec<Column<'a>>,
23    pub(crate) values: Row<'a>,
24}
25
26/// A builder for an `INSERT` statement for multiple rows.
27#[derive(Clone, Debug, PartialEq)]
28pub struct MultiRowInsert<'a> {
29    pub(crate) table: Option<Table<'a>>,
30    pub(crate) columns: Vec<Column<'a>>,
31    pub(crate) values: Vec<Row<'a>>,
32}
33
34/// `INSERT` conflict resolution strategies.
35#[allow(clippy::large_enum_variant)]
36#[derive(Clone, Debug, PartialEq)]
37pub enum OnConflict<'a> {
38    /// When a row already exists, do nothing.
39    DoNothing,
40    /// ON CONFLICT UPDATE is supported for Postgres
41    Update(Update<'a>, Vec<Column<'a>>),
42}
43
44impl<'a> From<Insert<'a>> for Query<'a> {
45    fn from(insert: Insert<'a>) -> Self {
46        Query::Insert(Box::new(insert))
47    }
48}
49
50impl<'a> From<SingleRowInsert<'a>> for Insert<'a> {
51    fn from(insert: SingleRowInsert<'a>) -> Self {
52        let values = if insert.values.is_empty() {
53            Expression::from(Row::new())
54        } else {
55            Expression::from(insert.values)
56        };
57
58        Insert {
59            table: insert.table,
60            columns: insert.columns,
61            values,
62            on_conflict: None,
63            returning: None,
64            comment: None,
65        }
66    }
67}
68
69impl<'a> From<MultiRowInsert<'a>> for Insert<'a> {
70    fn from(insert: MultiRowInsert<'a>) -> Self {
71        let values = Expression::from(Values::new(insert.values));
72
73        Insert {
74            table: insert.table,
75            columns: insert.columns,
76            values,
77            on_conflict: None,
78            returning: None,
79            comment: None,
80        }
81    }
82}
83
84impl<'a> From<SingleRowInsert<'a>> for Query<'a> {
85    fn from(insert: SingleRowInsert<'a>) -> Query<'a> {
86        Query::from(Insert::from(insert))
87    }
88}
89
90impl<'a> From<MultiRowInsert<'a>> for Query<'a> {
91    fn from(insert: MultiRowInsert<'a>) -> Query<'a> {
92        Query::from(Insert::from(insert))
93    }
94}
95
96impl<'a> Insert<'a> {
97    /// Creates a new single row `INSERT` statement for the given table.
98    ///
99    /// ```rust
100    /// # use grafbase_sql_ast::{ast::*, renderer::{Renderer, self}};
101    /// # fn main() {
102    /// let query = Insert::single_into("users");
103    /// let (sql, _) = renderer::Postgres::build(query);
104    ///
105    /// assert_eq!(r#"INSERT INTO "users" DEFAULT VALUES"#, sql);
106    /// # }
107    /// ```
108    pub fn single_into<T>(table: T) -> SingleRowInsert<'a>
109    where
110        T: Into<Table<'a>>,
111    {
112        SingleRowInsert {
113            table: Some(table.into()),
114            columns: Vec::new(),
115            values: Row::new(),
116        }
117    }
118
119    pub fn single() -> SingleRowInsert<'a> {
120        SingleRowInsert {
121            table: None,
122            columns: Vec::new(),
123            values: Row::new(),
124        }
125    }
126
127    /// Creates a new multi row `INSERT` statement for the given table.
128    pub fn multi_into<T, K, I>(table: T, columns: I) -> MultiRowInsert<'a>
129    where
130        T: Into<Table<'a>>,
131        K: Into<Column<'a>>,
132        I: IntoIterator<Item = K>,
133    {
134        MultiRowInsert {
135            table: Some(table.into()),
136            columns: columns.into_iter().map(|c| c.into()).collect(),
137            values: Vec::new(),
138        }
139    }
140
141    pub fn multi<K, I>(columns: I) -> MultiRowInsert<'a>
142    where
143        K: Into<Column<'a>>,
144        I: IntoIterator<Item = K>,
145    {
146        MultiRowInsert {
147            table: None,
148            columns: columns.into_iter().map(|c| c.into()).collect(),
149            values: Vec::new(),
150        }
151    }
152
153    pub fn expression_into<T, I, K, E>(table: T, columns: I, expression: E) -> Self
154    where
155        T: Into<Table<'a>>,
156        I: IntoIterator<Item = K>,
157        K: Into<Column<'a>>,
158        E: Into<Expression<'a>>,
159    {
160        Insert {
161            table: Some(table.into()),
162            columns: columns.into_iter().map(|c| c.into()).collect(),
163            values: expression.into(),
164            on_conflict: None,
165            returning: None,
166            comment: None,
167        }
168    }
169
170    /// Sets the conflict resolution strategy.
171    pub fn on_conflict(&mut self, on_conflict: OnConflict<'a>) {
172        self.on_conflict = Some(on_conflict);
173    }
174
175    /// Adds a comment to the insert.
176    pub fn comment<C: Into<Cow<'a, str>>>(&mut self, comment: C) {
177        self.comment = Some(comment.into());
178    }
179
180    /// Sets the returned columns.
181    #[cfg(any(feature = "postgresql", feature = "mssql", feature = "sqlite"))]
182    #[cfg_attr(
183        feature = "docs",
184        doc(cfg(any(feature = "postgresql", feature = "mssql", feature = "sqlite")))
185    )]
186    pub fn returning<K, I>(&mut self, columns: I)
187    where
188        K: Into<Column<'a>>,
189        I: IntoIterator<Item = K>,
190    {
191        self.returning = Some(columns.into_iter().map(|k| k.into()).collect());
192    }
193}
194
195impl<'a> SingleRowInsert<'a> {
196    /// Adds a new value to the `INSERT` statement
197    ///
198    /// ```rust
199    /// # use grafbase_sql_ast::{ast::*, renderer::{Renderer, self}};
200    /// # fn main() {
201    /// let mut query = Insert::single_into("users");
202    /// query.value("foo", 10);
203    ///
204    /// let (sql, params) = renderer::Postgres::build(query);
205    ///
206    /// assert_eq!(r#"INSERT INTO "users" ("foo") VALUES ($1)"#, sql);
207    /// assert_eq!(vec![Value::from(10)], params);
208    /// # }
209    /// ```
210    pub fn value<K, V>(&mut self, key: K, val: V)
211    where
212        K: Into<Column<'a>>,
213        V: Into<Expression<'a>>,
214    {
215        self.columns.push(key.into());
216        self.values.push(val.into());
217    }
218
219    /// Merge two single row inserts into a multi row insert.
220    ///
221    /// Both inserts must be to the same table and must include the same columns.
222    pub fn merge(self, other: SingleRowInsert<'a>) -> anyhow::Result<MultiRowInsert<'a>> {
223        if self.table != other.table {
224            return Err(anyhow!("Merging inserts must be on the same table.",));
225        }
226
227        if self.columns != other.columns {
228            return Err(anyhow!("All insert items must have the same columns."));
229        }
230
231        Ok(MultiRowInsert {
232            table: self.table,
233            columns: self.columns,
234            values: vec![self.values, other.values],
235        })
236    }
237
238    /// Convert into a common `Insert` statement.
239    pub fn build(self) -> Insert<'a> {
240        Insert::from(self)
241    }
242}
243
244impl<'a> MultiRowInsert<'a> {
245    /// Adds multiple new rows to be inserted.
246    ///
247    /// ```rust
248    /// # use grafbase_sql_ast::{ast::*, renderer::{Renderer, self}};
249    /// # fn main() {
250    /// let mut query = Insert::multi_into("users", vec!["foo"]);
251    ///
252    /// query.values(vec![1]);
253    /// query.values(vec![2]);
254    ///
255    /// let (sql, params) = renderer::Postgres::build(query);
256    ///
257    /// assert_eq!(r#"INSERT INTO "users" ("foo") VALUES ($1), ($2)"#, sql);
258    ///
259    /// assert_eq!(
260    ///     vec![
261    ///         Value::from(1),
262    ///         Value::from(2),
263    ///     ], params);
264    /// # }
265    /// ```
266    pub fn values<V>(&mut self, values: V)
267    where
268        V: Into<Row<'a>>,
269    {
270        self.values.push(values.into());
271    }
272
273    /// Extend the insert statement with a single row insert.
274    ///
275    /// Both inserts must be to the same table and must include the same columns.
276    pub fn extend(&mut self, other: SingleRowInsert<'a>) -> anyhow::Result<()> {
277        if self.table != other.table {
278            return Err(anyhow!("Merging inserts must be on the same table.",));
279        }
280
281        if self.columns != other.columns {
282            return Err(anyhow!("All insert items must have the same columns."));
283        }
284
285        self.values.push(other.values);
286
287        Ok(())
288    }
289
290    /// Convert into a common `Insert` statement.
291    pub fn build(self) -> Insert<'a> {
292        Insert::from(self)
293    }
294}