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}