Skip to main content

keelson_psql/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::Psql;
10
11/// A PostgreSQL `UPDATE`.
12///
13/// From <https://www.postgresql.org/docs/17/sql-update.html>:
14///
15/// ```text
16/// [ WITH [ RECURSIVE ] with_query [, ...] ]
17/// UPDATE [ ONLY ] table_name [ * ] [ [ AS ] alias ]
18///     SET { column_name = { expression | DEFAULT }
19///         | ( column_name [, ...] ) = [ ROW ] ( { expression | DEFAULT } [, ...] )
20///         | ( column_name [, ...] ) = ( sub-SELECT ) } [, ...]
21///     [ FROM from_item [, ...] ]
22///     [ WHERE condition | WHERE CURRENT OF cursor_name ]
23///     [ RETURNING … ]
24/// ```
25///
26/// `ONLY` lives on [`TableRef::only`], because that is where the shared clause puts
27/// it and because the same flag decorates a `FROM` item.
28///
29/// The assignment list is not optional: `UPDATE t` with no `SET` is not a statement,
30/// so an empty [`Set`] is a recorded [`Error::Incomplete`] rather than a clause that
31/// renders nothing.
32#[derive(Debug, Clone, Default)]
33pub struct UpdateQuery {
34    /// `WITH …`.
35    pub with: With,
36    /// The table being updated.
37    pub table: TableRef,
38    /// `SET …`.
39    pub set: Set,
40    /// The first `FROM` item, with its joins.
41    pub from: TableRef,
42    /// Further comma-separated `FROM` items.
43    pub extra_from: Vec<TableRef>,
44    /// `WHERE …`.
45    pub where_: Where,
46    /// `RETURNING …`.
47    pub returning: Returning,
48}
49
50impl UpdateQuery {
51    /// An `UPDATE` with nothing set yet.
52    pub fn new() -> UpdateQuery {
53        UpdateQuery::default()
54    }
55
56    /// Apply more mods to an existing query.
57    pub fn apply(&mut self, mods: impl Mod<UpdateQuery>) {
58        mods.apply(self);
59    }
60}
61
62impl Expression for UpdateQuery {
63    fn write_sql(&self, w: &mut SqlWriter<'_>) {
64        w.write_if(!self.with.is_empty(), "", &self.with, " ");
65
66        if self.table.is_empty() {
67            w.record_error(Error::Incomplete("the table of an UPDATE"));
68            return;
69        }
70        if self.set.is_empty() {
71            w.record_error(Error::Incomplete("the assignments of an UPDATE"));
72            return;
73        }
74
75        w.push_str("UPDATE ");
76        w.write_expr(&self.table);
77        // `Set` writes no keyword of its own — MySQL's ON DUPLICATE KEY UPDATE
78        // takes the same list bare — so the `SET` belongs here.
79        w.push_str(" SET ");
80        w.write_expr(&self.set);
81
82        write_from_list(
83            w,
84            " FROM ",
85            &self.from,
86            &self.extra_from,
87            "the FROM item its joins attach to",
88        );
89
90        w.write_if(!self.where_.is_empty(), " ", &self.where_, "");
91        w.write_if(!self.returning.is_empty(), " ", &self.returning, "");
92    }
93}
94
95impl Query for UpdateQuery {
96    fn query_type(&self) -> QueryType {
97        QueryType::Update
98    }
99
100    fn dialect(&self) -> &dyn Dialect {
101        &Psql
102    }
103}
104
105impl<H, L, M> QueryExtensions<H, L, M> for UpdateQuery {}
106
107impl IntoExpr for UpdateQuery {
108    fn into_expr(self) -> Expr {
109        crate::query(self)
110    }
111}
112
113impl IntoExprList for UpdateQuery {
114    fn into_expr_list(self) -> Vec<Expr> {
115        vec![self.into_expr()]
116    }
117}
118
119impl HasWith for UpdateQuery {
120    fn with_mut(&mut self) -> &mut With {
121        &mut self.with
122    }
123}
124
125impl HasTargetTable for UpdateQuery {
126    fn target_table_mut(&mut self) -> &mut TableRef {
127        &mut self.table
128    }
129}
130
131impl HasSet for UpdateQuery {
132    fn set_mut(&mut self) -> &mut Set {
133        &mut self.set
134    }
135}
136
137impl HasTableRef for UpdateQuery {
138    fn table_ref_mut(&mut self) -> &mut TableRef {
139        &mut self.from
140    }
141}
142
143impl HasExtraTables for UpdateQuery {
144    fn extra_tables_mut(&mut self) -> &mut Vec<TableRef> {
145        &mut self.extra_from
146    }
147}
148
149impl HasJoins for UpdateQuery {
150    fn joins_mut(&mut self) -> &mut Vec<Join> {
151        &mut self.from.joins
152    }
153}
154
155impl HasWhere for UpdateQuery {
156    fn where_mut(&mut self) -> &mut Where {
157        &mut self.where_
158    }
159}
160
161impl HasReturning for UpdateQuery {
162    fn returning_mut(&mut self) -> &mut Returning {
163        &mut self.returning
164    }
165}