Skip to main content

keelson_mysql/statement/
insert.rs

1use keelson_core::clause::{HasSet, HasTableRef, HasValues, Set, TableRef, Values};
2use keelson_core::expr::{Expr, IntoExpr, IntoExprList};
3use keelson_core::{Dialect, Error, Expression, Mod, Query, QueryExtensions, QueryType, SqlWriter};
4
5use super::write_hints_and_modifiers;
6use crate::Mysql;
7use crate::extras::{
8    HasDuplicateKeyUpdate, HasHints, HasModifiers, HasRowAlias, Hints, Modifiers, RowAlias,
9};
10
11/// A MySQL `INSERT`.
12///
13/// From <https://dev.mysql.com/doc/refman/8.4/en/insert.html>, with the three row
14/// sources folded together:
15///
16/// ```text
17/// INSERT [hint_comment] [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE]
18///     [INTO] tbl_name
19///     [PARTITION (partition_name [, partition_name] ...)]
20///     [(col_name [, col_name] ...)]
21///     { {VALUES | VALUE} (value_list) [, (value_list)] ...
22///     | SET assignment_list
23///     | [(col_name [, col_name] ...)] { SELECT ... | TABLE tbl | VALUES row_list } }
24///     [AS row_alias[(col_alias [, col_alias] ...)]]
25///     [ON DUPLICATE KEY UPDATE assignment_list]
26/// ```
27///
28/// `INTO` is written unconditionally even though the grammar makes it optional:
29/// the shorter form saves four characters and costs a reader a double-take.
30///
31/// # Three row sources, one field each
32///
33/// `VALUES`, a query, and `SET` are alternatives. [`Values`] holds the first two;
34/// [`set`](Self::set) is the third, and it **wins** when both are present, because
35/// a half-and-half rendering is not a statement. With none of them, `VALUES ()` is
36/// written — MySQL's spelling of "every column takes its default", which
37/// PostgreSQL spells `DEFAULT VALUES`.
38///
39/// Choosing `SET` also drops the insert column list, because the `SET` production
40/// does not have one: `INSERT INTO t (a) SET b = 1` is a syntax error, not a
41/// statement with a redundant clause. `PARTITION` is in both productions and stays.
42///
43/// # No `WITH`
44///
45/// MySQL permits a CTE only immediately before the `SELECT` of an
46/// `INSERT … SELECT`, never in front of the `INSERT` (*15.2.20*). So there is no
47/// `with` field and no `insert::with` mod; put the `WITH` on the sub-query given
48/// to [`insert::query`](crate::insert::query).
49#[derive(Debug, Clone, Default)]
50pub struct InsertQuery {
51    /// `/*+ … */`.
52    pub hints: Hints,
53    /// `LOW_PRIORITY` / `HIGH_PRIORITY` / `DELAYED`, and `IGNORE`.
54    pub modifiers: Modifiers,
55    /// The target: table, partitions, and the insert column list.
56    pub table: TableRef,
57    /// The rows, or the query to insert the results of.
58    pub values: Values,
59    /// `SET a = 1, b = 2`, the assignment-list row source.
60    pub set: Set,
61    /// `AS row_alias [(cols)]`.
62    pub row_alias: RowAlias,
63    /// `ON DUPLICATE KEY UPDATE …`.
64    pub duplicate_key_update: Set,
65}
66
67impl InsertQuery {
68    /// An `INSERT` with nothing set yet.
69    pub fn new() -> InsertQuery {
70        InsertQuery::default()
71    }
72
73    /// Apply more mods to an existing query.
74    pub fn apply(&mut self, mods: impl Mod<InsertQuery>) {
75        mods.apply(self);
76    }
77}
78
79impl Expression for InsertQuery {
80    fn write_sql(&self, w: &mut SqlWriter<'_>) {
81        if self.table.is_empty() {
82            // Unlike a `SELECT`, which is a statement without a table, an `INSERT`
83            // has nowhere to put the rows.
84            w.record_error(Error::Incomplete("the target table of an INSERT"));
85            return;
86        }
87
88        w.push_str("INSERT ");
89        write_hints_and_modifiers(w, &self.hints, &self.modifiers);
90        w.push_str("INTO ");
91        write_target_and_row_source(w, &self.table, &self.set, &self.values);
92
93        w.write_if(!self.row_alias.is_empty(), " ", &self.row_alias, "");
94        w.write_if(
95            !self.duplicate_key_update.is_empty(),
96            " ON DUPLICATE KEY UPDATE ",
97            &self.duplicate_key_update,
98            "",
99        );
100    }
101}
102
103/// The target and the row source, shared by `INSERT` and `REPLACE`.
104///
105/// They are written together because the choice of row source decides whether the
106/// target carries its column list: `SET` wins over `VALUES`, and the `SET`
107/// production has no `(col_name, …)`. An absent source is `VALUES ()` — see
108/// [`InsertQuery`].
109pub(super) fn write_target_and_row_source(
110    w: &mut SqlWriter<'_>,
111    table: &TableRef,
112    set: &Set,
113    values: &Values,
114) {
115    if set.is_empty() {
116        w.write_expr(table);
117        if values.is_empty() {
118            w.push_str(" VALUES ()");
119        } else {
120            w.push_str(" ");
121            w.write_expr(values);
122        }
123        return;
124    }
125
126    // Only the SET path pays for the clone, and only to drop a list the production
127    // it selected does not have.
128    if table.columns.is_empty() {
129        w.write_expr(table);
130    } else {
131        w.write_expr(&TableRef {
132            columns: Vec::new(),
133            ..table.clone()
134        });
135    }
136    w.push_str(" SET ");
137    w.write_expr(set);
138}
139
140impl Query for InsertQuery {
141    fn query_type(&self) -> QueryType {
142        QueryType::Insert
143    }
144
145    fn dialect(&self) -> &dyn Dialect {
146        &Mysql
147    }
148}
149
150impl<H, L, M> QueryExtensions<H, L, M> for InsertQuery {}
151
152impl IntoExpr for InsertQuery {
153    fn into_expr(self) -> Expr {
154        crate::query(self)
155    }
156}
157
158impl IntoExprList for InsertQuery {
159    fn into_expr_list(self) -> Vec<Expr> {
160        vec![self.into_expr()]
161    }
162}
163
164impl HasHints for InsertQuery {
165    fn hints_mut(&mut self) -> &mut Hints {
166        &mut self.hints
167    }
168}
169
170impl HasModifiers for InsertQuery {
171    fn modifiers_mut(&mut self) -> &mut Modifiers {
172        &mut self.modifiers
173    }
174}
175
176impl HasTableRef for InsertQuery {
177    fn table_ref_mut(&mut self) -> &mut TableRef {
178        &mut self.table
179    }
180}
181
182impl HasValues for InsertQuery {
183    fn values_mut(&mut self) -> &mut Values {
184        &mut self.values
185    }
186}
187
188/// The `INSERT … SET` row source, *not* the `ON DUPLICATE KEY UPDATE` list —
189/// see [`HasDuplicateKeyUpdate`].
190impl HasSet for InsertQuery {
191    fn set_mut(&mut self) -> &mut Set {
192        &mut self.set
193    }
194}
195
196impl HasRowAlias for InsertQuery {
197    fn row_alias_mut(&mut self) -> &mut RowAlias {
198        &mut self.row_alias
199    }
200}
201
202impl HasDuplicateKeyUpdate for InsertQuery {
203    fn duplicate_key_update_mut(&mut self) -> &mut Set {
204        &mut self.duplicate_key_update
205    }
206}