Skip to main content

keelson_sqlite/statement/
update.rs

1use keelson_core::clause::{
2    HasJoins, HasReturning, HasSet, HasTableRef, HasWhere, HasWith, Join, Returning, Set, TableRef,
3    Where, With,
4};
5use keelson_core::expr::{Expr, IntoExpr, IntoExprList};
6use keelson_core::{Dialect, Error, Expression, Mod, Query, QueryExtensions, QueryType, SqlWriter};
7
8use super::{HasExtraTables, HasTargetTable, write_from_list};
9use crate::Sqlite;
10use crate::extras::{HasOr, Or};
11
12/// A SQLite `UPDATE`.
13///
14/// From <https://www.sqlite.org/lang_update.html>:
15///
16/// ```text
17/// [ WITH [ RECURSIVE ] common-table-expression [, ...] ]
18/// UPDATE [ OR { ROLLBACK | ABORT | REPLACE | FAIL | IGNORE } ] qualified-table-name
19///     SET { column | ( column [, ...] ) } = expr [, ...]
20///     [ FROM table-or-subquery [, ...] | join-clause ]
21///     [ WHERE expr ]
22///     [ RETURNING result-column [, ...] ]
23/// ```
24///
25/// `UPDATE … FROM` needs SQLite 3.33 or later and `RETURNING` needs 3.35.
26///
27/// The target is a `qualified-table-name`, so it takes `INDEXED BY`/`NOT INDEXED`
28/// but **not** a column-alias list — the alias list SQLite allows there is only
29/// `AS alias`. The `FROM` items are `table-or-subquery`s and the joins attach to
30/// *those*, never to the target, which is the whole reason
31/// [`table`](crate::update::table) and [`from`](crate::update::from) are different
32/// mods.
33///
34/// The assignment list is not optional: `UPDATE t` with no `SET` is not a
35/// statement, so an empty [`Set`] is a recorded [`Error::Incomplete`].
36///
37/// There is no `ORDER BY` or `LIMIT` here. SQLite's parser accepts them, but only a
38/// build compiled with `SQLITE_ENABLE_UPDATE_DELETE_LIMIT` does, and the ordinary
39/// one refuses them.
40#[derive(Debug, Clone, Default)]
41pub struct UpdateQuery {
42    /// `WITH …`.
43    pub with: With,
44    /// `OR REPLACE` and friends. `None` is the default, `ABORT`.
45    pub or: Option<Or>,
46    /// The table being updated.
47    pub table: TableRef,
48    /// `SET …`.
49    pub set: Set,
50    /// The first `FROM` item, with its joins.
51    pub from: TableRef,
52    /// Further comma-separated `FROM` items.
53    pub extra_from: Vec<TableRef>,
54    /// `WHERE …`.
55    pub where_: Where,
56    /// `RETURNING …`.
57    pub returning: Returning,
58}
59
60impl UpdateQuery {
61    /// An `UPDATE` with nothing set yet.
62    pub fn new() -> UpdateQuery {
63        UpdateQuery::default()
64    }
65
66    /// Apply more mods to an existing query.
67    pub fn apply(&mut self, mods: impl Mod<UpdateQuery>) {
68        mods.apply(self);
69    }
70}
71
72impl Expression for UpdateQuery {
73    fn write_sql(&self, w: &mut SqlWriter<'_>) {
74        w.write_if(!self.with.is_empty(), "", &self.with, " ");
75
76        if self.table.is_empty() {
77            w.record_error(Error::Incomplete("the table of an UPDATE"));
78            return;
79        }
80        if self.set.is_empty() {
81            w.record_error(Error::Incomplete("the assignments of an UPDATE"));
82            return;
83        }
84
85        w.push_str("UPDATE");
86        if let Some(or) = self.or {
87            w.push_str(" OR ");
88            w.push_str(or.as_str());
89        }
90        w.push_str(" ");
91        w.write_expr(&self.table);
92        // `Set` writes no keyword of its own — MySQL's ON DUPLICATE KEY UPDATE takes
93        // the same list bare — so the `SET` belongs here.
94        w.push_str(" SET ");
95        w.write_expr(&self.set);
96
97        write_from_list(
98            w,
99            " FROM ",
100            &self.from,
101            &self.extra_from,
102            "the FROM item its joins attach to",
103        );
104
105        w.write_if(!self.where_.is_empty(), " ", &self.where_, "");
106        w.write_if(!self.returning.is_empty(), " ", &self.returning, "");
107    }
108}
109
110impl Query for UpdateQuery {
111    fn query_type(&self) -> QueryType {
112        QueryType::Update
113    }
114
115    fn dialect(&self) -> &dyn Dialect {
116        &Sqlite
117    }
118}
119
120impl<H, L, M> QueryExtensions<H, L, M> for UpdateQuery {}
121
122impl IntoExpr for UpdateQuery {
123    fn into_expr(self) -> Expr {
124        crate::query(self)
125    }
126}
127
128impl IntoExprList for UpdateQuery {
129    fn into_expr_list(self) -> Vec<Expr> {
130        vec![self.into_expr()]
131    }
132}
133
134impl HasWith for UpdateQuery {
135    fn with_mut(&mut self) -> &mut With {
136        &mut self.with
137    }
138}
139
140impl HasOr for UpdateQuery {
141    fn or_mut(&mut self) -> &mut Option<Or> {
142        &mut self.or
143    }
144}
145
146impl HasTargetTable for UpdateQuery {
147    fn target_table_mut(&mut self) -> &mut TableRef {
148        &mut self.table
149    }
150}
151
152impl HasSet for UpdateQuery {
153    fn set_mut(&mut self) -> &mut Set {
154        &mut self.set
155    }
156}
157
158impl HasTableRef for UpdateQuery {
159    fn table_ref_mut(&mut self) -> &mut TableRef {
160        &mut self.from
161    }
162}
163
164impl HasExtraTables for UpdateQuery {
165    fn extra_tables_mut(&mut self) -> &mut Vec<TableRef> {
166        &mut self.extra_from
167    }
168}
169
170impl HasJoins for UpdateQuery {
171    fn joins_mut(&mut self) -> &mut Vec<Join> {
172        &mut self.from.joins
173    }
174}
175
176impl HasWhere for UpdateQuery {
177    fn where_mut(&mut self) -> &mut Where {
178        &mut self.where_
179    }
180}
181
182impl HasReturning for UpdateQuery {
183    fn returning_mut(&mut self) -> &mut Returning {
184        &mut self.returning
185    }
186}