Skip to main content

keelson_mysql/statement/
delete.rs

1use std::borrow::Cow;
2
3use keelson_core::clause::{
4    HasJoins, HasLimit, HasOrderBy, HasTableRef, HasWhere, HasWith, Join, Limit, OrderBy, TableRef,
5    Where, With,
6};
7use keelson_core::expr::{Expr, IntoExpr, IntoExprList};
8use keelson_core::{Dialect, Error, Expression, Mod, Query, QueryExtensions, QueryType, SqlWriter};
9
10use super::{HasDeleteTables, HasExtraTables, write_hints_and_modifiers, write_table_list};
11use crate::Mysql;
12use crate::extras::{HasHints, HasModifiers, Hints, Modifiers};
13
14/// A MySQL `DELETE`.
15///
16/// From <https://dev.mysql.com/doc/refman/8.4/en/delete.html>, taking the
17/// single-table form and the `FROM … USING` spelling of the multiple-table one:
18///
19/// ```text
20/// [WITH [RECURSIVE] with_query [, ...]]
21/// DELETE [hint_comment] [LOW_PRIORITY] [QUICK] [IGNORE]
22///     FROM tbl_name[.*] [[AS] tbl_alias] [, tbl_name[.*]] ...
23///     [PARTITION (partition_name [, partition_name] ...)]
24///     [USING table_references]
25///     [WHERE where_condition]
26///     [ORDER BY ...]
27///     [LIMIT row_count]
28/// ```
29///
30/// MySQL spells the multiple-table delete two ways —
31/// `DELETE t1, t2 FROM refs` and `DELETE FROM t1, t2 USING refs`. Only the second
32/// is built here, because it is also the single-table form with the `USING` left
33/// out, so one shape covers both and `DELETE FROM t` is what an empty `USING`
34/// gives.
35///
36/// # `PARTITION` sits in an odd place
37///
38/// This is the one statement where MySQL writes `PARTITION` *after* the alias.
39/// [`TableRef`] puts it before, which is right everywhere else, so
40/// [`HasDeleteTables`] carries a partition slot of its own and the
41/// `delete::from(..).partition(..)` chain moves its list into it.
42///
43/// `ORDER BY` and `LIMIT`, like in `UPDATE`, are single-table only.
44#[derive(Debug, Clone, Default)]
45pub struct DeleteQuery {
46    /// `WITH …`.
47    pub with: With,
48    /// `/*+ … */`.
49    pub hints: Hints,
50    /// `LOW_PRIORITY`, `QUICK` and `IGNORE`.
51    pub modifiers: Modifiers,
52    /// The tables rows are deleted from — the `FROM` list.
53    pub tables: Vec<TableRef>,
54    /// `PARTITION (…)`, written after the `FROM` list.
55    pub partitions: Vec<Cow<'static, str>>,
56    /// The first `USING` item, with its joins.
57    pub using: TableRef,
58    /// Further comma-separated `USING` items.
59    pub extra_using: Vec<TableRef>,
60    /// `WHERE …`.
61    pub where_: Where,
62    /// `ORDER BY …`. Single-table form only.
63    pub order_by: OrderBy,
64    /// `LIMIT row_count`. Single-table form only, and there is no `OFFSET`.
65    pub limit: Limit,
66}
67
68impl DeleteQuery {
69    /// A `DELETE` with nothing set yet.
70    pub fn new() -> DeleteQuery {
71        DeleteQuery::default()
72    }
73
74    /// Apply more mods to an existing query.
75    pub fn apply(&mut self, mods: impl Mod<DeleteQuery>) {
76        mods.apply(self);
77    }
78}
79
80impl Expression for DeleteQuery {
81    fn write_sql(&self, w: &mut SqlWriter<'_>) {
82        w.write_if(!self.with.is_empty(), "", &self.with, " ");
83
84        let mut tables = self.tables.iter().filter(|t| !t.is_empty());
85        let Some(first) = tables.next() else {
86            w.record_error(Error::Incomplete("the table of a DELETE"));
87            return;
88        };
89
90        w.push_str("DELETE ");
91        write_hints_and_modifiers(w, &self.hints, &self.modifiers);
92        w.push_str("FROM ");
93        w.write_expr(first);
94        for table in tables {
95            w.push_str(", ");
96            w.write_expr(table);
97        }
98
99        if !self.partitions.is_empty() {
100            w.push_str(" PARTITION (");
101            for (i, partition) in self.partitions.iter().enumerate() {
102                if i > 0 {
103                    w.push_str(", ");
104                }
105                w.push_quoted(&[partition]);
106            }
107            w.push_str(")");
108        }
109
110        write_table_list(
111            w,
112            " USING ",
113            &self.using,
114            &self.extra_using,
115            "the USING item its joins attach to",
116        );
117
118        w.write_if(!self.where_.is_empty(), " ", &self.where_, "");
119        w.write_if(!self.order_by.is_empty(), " ", &self.order_by, "");
120        w.write_if(!self.limit.is_empty(), " ", &self.limit, "");
121    }
122}
123
124impl Query for DeleteQuery {
125    fn query_type(&self) -> QueryType {
126        QueryType::Delete
127    }
128
129    fn dialect(&self) -> &dyn Dialect {
130        &Mysql
131    }
132}
133
134impl<H, L, M> QueryExtensions<H, L, M> for DeleteQuery {}
135
136impl IntoExpr for DeleteQuery {
137    fn into_expr(self) -> Expr {
138        crate::query(self)
139    }
140}
141
142impl IntoExprList for DeleteQuery {
143    fn into_expr_list(self) -> Vec<Expr> {
144        vec![self.into_expr()]
145    }
146}
147
148impl HasWith for DeleteQuery {
149    fn with_mut(&mut self) -> &mut With {
150        &mut self.with
151    }
152}
153
154impl HasHints for DeleteQuery {
155    fn hints_mut(&mut self) -> &mut Hints {
156        &mut self.hints
157    }
158}
159
160impl HasModifiers for DeleteQuery {
161    fn modifiers_mut(&mut self) -> &mut Modifiers {
162        &mut self.modifiers
163    }
164}
165
166impl HasDeleteTables for DeleteQuery {
167    fn delete_tables_mut(&mut self) -> &mut Vec<TableRef> {
168        &mut self.tables
169    }
170
171    fn delete_partitions_mut(&mut self) -> &mut Vec<Cow<'static, str>> {
172        &mut self.partitions
173    }
174}
175
176impl HasTableRef for DeleteQuery {
177    fn table_ref_mut(&mut self) -> &mut TableRef {
178        &mut self.using
179    }
180}
181
182impl HasExtraTables for DeleteQuery {
183    fn extra_tables_mut(&mut self) -> &mut Vec<TableRef> {
184        &mut self.extra_using
185    }
186}
187
188impl HasJoins for DeleteQuery {
189    fn joins_mut(&mut self) -> &mut Vec<Join> {
190        &mut self.using.joins
191    }
192}
193
194impl HasWhere for DeleteQuery {
195    fn where_mut(&mut self) -> &mut Where {
196        &mut self.where_
197    }
198}
199
200impl HasOrderBy for DeleteQuery {
201    fn order_by_mut(&mut self) -> &mut OrderBy {
202        &mut self.order_by
203    }
204}
205
206impl HasLimit for DeleteQuery {
207    fn limit_mut(&mut self) -> &mut Limit {
208        &mut self.limit
209    }
210}