Skip to main content

keelson_sqlite/statement/
insert.rs

1use keelson_core::clause::{
2    ConflictClause, HasReturning, HasTableRef, HasValues, HasWith, Returning, TableRef, Values,
3    With,
4};
5use keelson_core::expr::{Expr, IntoExpr, IntoExprList};
6use keelson_core::{Dialect, Error, Expression, Mod, Query, QueryExtensions, QueryType, SqlWriter};
7
8use crate::Sqlite;
9use crate::extras::{HasOr, HasUpserts, Or, write_spaced};
10
11/// A SQLite `INSERT`.
12///
13/// From <https://www.sqlite.org/lang_insert.html>:
14///
15/// ```text
16/// [ WITH [ RECURSIVE ] common-table-expression [, ...] ]
17/// INSERT [ OR { ROLLBACK | ABORT | REPLACE | FAIL | IGNORE } ]
18///     INTO [ schema. ] table [ AS alias ] [ ( column [, ...] ) ]
19///     { VALUES ( expr [, ...] ) [, ...] [ upsert-clause ]
20///     | select-stmt [ upsert-clause ]
21///     | DEFAULT VALUES }
22///     [ RETURNING result-column [, ...] ]
23/// ```
24///
25/// `DEFAULT VALUES` is the alternative [`Values`] cannot represent — an absent
26/// clause has to render nothing — so an empty `Values` is what `DEFAULT VALUES`
27/// *is* here, and this query writes the keywords itself. Note from the grammar that
28/// `DEFAULT VALUES` admits no `upsert-clause`; a row of defaults conflicts with
29/// nothing worth updating, and SQLite refuses the combination.
30///
31/// A source `select-stmt` with an `upsert-clause` after it must have a `WHERE`,
32/// even a trivial one, or the parser reads the `ON` as the start of a join
33/// condition. That is SQLite's rule, not this type's, and it is left to the parser
34/// to say so — with an unusually clear message.
35#[derive(Debug, Clone, Default)]
36pub struct InsertQuery {
37    /// `WITH …`.
38    pub with: With,
39    /// `OR REPLACE` and friends. `None` is the default, `ABORT`.
40    pub or: Option<Or>,
41    /// The target: table, optional alias, and the insert column list.
42    pub table: TableRef,
43    /// The rows, or the query to insert the results of. Empty means
44    /// `DEFAULT VALUES`.
45    pub values: Values,
46    /// The `upsert-clause` list. SQLite 3.35 and later accept several, tried in
47    /// order, and only the last may omit its conflict target.
48    pub upserts: Vec<ConflictClause>,
49    /// `RETURNING …`. SQLite 3.35 and later.
50    pub returning: Returning,
51}
52
53impl InsertQuery {
54    /// An `INSERT` with nothing set yet.
55    pub fn new() -> InsertQuery {
56        InsertQuery::default()
57    }
58
59    /// Apply more mods to an existing query.
60    pub fn apply(&mut self, mods: impl Mod<InsertQuery>) {
61        mods.apply(self);
62    }
63}
64
65impl Expression for InsertQuery {
66    fn write_sql(&self, w: &mut SqlWriter<'_>) {
67        w.write_if(!self.with.is_empty(), "", &self.with, " ");
68
69        if self.table.is_empty() {
70            // Unlike a `SELECT`, which is a statement without a table, an `INSERT`
71            // has nowhere to put the rows.
72            w.record_error(Error::Incomplete("the target table of an INSERT"));
73            return;
74        }
75
76        w.push_str("INSERT");
77        if let Some(or) = self.or {
78            w.push_str(" OR ");
79            w.push_str(or.as_str());
80        }
81        w.push_str(" INTO ");
82        w.write_expr(&self.table);
83
84        if self.values.is_empty() {
85            // Read the grammar's braces: the `upsert-clause` hangs off the `VALUES`
86            // and `select-stmt` alternatives only. `DEFAULT VALUES ON CONFLICT …`
87            // does not parse — a row of defaults conflicts with nothing worth
88            // updating — so it is refused rather than written.
89            if self.upserts.iter().any(|c| !c.is_empty()) {
90                w.record_error(Error::other(
91                    "a DEFAULT VALUES insert cannot carry an ON CONFLICT clause",
92                ));
93                return;
94            }
95            w.push_str(" DEFAULT VALUES");
96        } else {
97            w.push_str(" ");
98            w.write_expr(&self.values);
99        }
100
101        // An actionless clause renders nothing, so it must not bring the space in
102        // front of it either — `… VALUES (?1) RETURNING *` would otherwise be
103        // written with two.
104        if self.upserts.iter().any(|c| !c.is_empty()) {
105            w.push_str(" ");
106            write_spaced(w, self.upserts.iter().filter(|c| !c.is_empty()));
107        }
108
109        w.write_if(!self.returning.is_empty(), " ", &self.returning, "");
110    }
111}
112
113impl Query for InsertQuery {
114    fn query_type(&self) -> QueryType {
115        QueryType::Insert
116    }
117
118    fn dialect(&self) -> &dyn Dialect {
119        &Sqlite
120    }
121}
122
123impl<H, L, M> QueryExtensions<H, L, M> for InsertQuery {}
124
125impl IntoExpr for InsertQuery {
126    fn into_expr(self) -> Expr {
127        crate::query(self)
128    }
129}
130
131impl IntoExprList for InsertQuery {
132    fn into_expr_list(self) -> Vec<Expr> {
133        vec![self.into_expr()]
134    }
135}
136
137impl HasWith for InsertQuery {
138    fn with_mut(&mut self) -> &mut With {
139        &mut self.with
140    }
141}
142
143impl HasTableRef for InsertQuery {
144    fn table_ref_mut(&mut self) -> &mut TableRef {
145        &mut self.table
146    }
147}
148
149impl HasValues for InsertQuery {
150    fn values_mut(&mut self) -> &mut Values {
151        &mut self.values
152    }
153}
154
155impl HasOr for InsertQuery {
156    fn or_mut(&mut self) -> &mut Option<Or> {
157        &mut self.or
158    }
159}
160
161impl HasUpserts for InsertQuery {
162    fn upserts_mut(&mut self) -> &mut Vec<ConflictClause> {
163        &mut self.upserts
164    }
165}
166
167impl HasReturning for InsertQuery {
168    fn returning_mut(&mut self) -> &mut Returning {
169        &mut self.returning
170    }
171}