Skip to main content

qbrs_core/
update.rs

1//! `UPDATE .. SET .. WHERE ..`.
2
3use std::marker::PhantomData;
4
5use crate::dialect::Dialect;
6use crate::expr::{AssignsTo, Column, ColumnKey, ExprKind, IntoExpr, Value, Writable};
7use crate::render::{Sink, render_and_list, render_expr, render_ident};
8use crate::scope::{BaseTable, Superset, Table};
9use crate::select::{Condition, Predicate};
10use crate::statement::{Statement, WrittenTable};
11
12/// Implemented by the `#[derive(Table)]`-generated `*Update` struct: every
13/// field is optional (untouched vs. touched), and doubly-optional for
14/// nullable columns (untouched vs. explicit NULL vs. explicit value).
15/// `sets()` returns only the touched `(column, value)` pairs.
16pub trait UpdateRow: private::Sealed {
17    type Table: Table;
18    fn sets(self) -> Vec<(&'static str, Value)>;
19}
20
21mod private {
22    /// `sets()` names columns of `Table` by string; `#[derive(Table)]` is
23    /// what guarantees they exist, so it is the only thing that can produce
24    /// an `UpdateRow`.
25    pub trait Sealed {}
26}
27
28#[doc(hidden)]
29pub use private::Sealed as UpdateRowSealed;
30
31/// A `SET` list that is known non-empty, which is the only kind that has a
32/// SQL form. Every `*Update` derives `Default`, and that value (what a
33/// PATCH handler holds when the request changed nothing) has no assignments
34/// at all. The check therefore belongs where such a value enters a
35/// statement rather than at rendering time.
36pub struct Assignments<T> {
37    sets: Vec<(&'static str, ExprKind)>,
38    _marker: PhantomData<fn() -> T>,
39}
40
41// Hand-written for the reason `Expr`'s are: a derive would ask the phantom
42// table marker to be `Clone`/`Debug`, and a schema's marker is a bare unit
43// struct, so the derived impls would apply to no table at all.
44impl<T> Clone for Assignments<T> {
45    fn clone(&self) -> Self {
46        Assignments {
47            sets: self.sets.clone(),
48            _marker: PhantomData,
49        }
50    }
51}
52
53impl<T> std::fmt::Debug for Assignments<T> {
54    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55        f.debug_struct("Assignments")
56            .field("sets", &self.sets)
57            .finish()
58    }
59}
60
61impl<T: Table> Assignments<T> {
62    /// `column = <expression>`. This is for the assignments a value can't
63    /// say: `updated_at = now()`, `version = version + 1`. The expression is
64    /// checked against the table being written to, exactly as a `WHERE`
65    /// condition is.
66    pub fn set_to<C, V, Idxs>(_column: Column<C>, value: V) -> Self
67    where
68        C: ColumnKey<Table = T> + Writable,
69        V: IntoExpr,
70        V::Sql: AssignsTo<C::Sql>,
71        WrittenTable<T>: Superset<V::Req, Idxs>,
72    {
73        Assignments {
74            sets: vec![(<C as crate::row::Named>::NAME, value.into_expr().kind)],
75            _marker: PhantomData,
76        }
77    }
78
79    /// One more of them, so a statement can assign several expressions.
80    pub fn and_set_to<C, V, Idxs>(mut self, _column: Column<C>, value: V) -> Self
81    where
82        C: ColumnKey<Table = T> + Writable,
83        V: IntoExpr,
84        V::Sql: AssignsTo<C::Sql>,
85        WrittenTable<T>: Superset<V::Req, Idxs>,
86    {
87        push_set(
88            &mut self.sets,
89            <C as crate::row::Named>::NAME,
90            value.into_expr().kind,
91        );
92        self
93    }
94}
95
96/// A column assigned twice is not a statement any database accepts, and
97/// layering a computed assignment over a request's is exactly when it
98/// happens, so the later one replaces the earlier. Shared with the
99/// `ON CONFLICT DO UPDATE` list, which is built the same way.
100pub(crate) fn push_set(
101    sets: &mut Vec<(&'static str, ExprKind)>,
102    name: &'static str,
103    value: ExprKind,
104) {
105    sets.retain(|(col, _)| *col != name);
106    sets.push((name, value));
107}
108
109impl<T> Assignments<T> {
110    /// The list itself, for the one other statement that renders a `SET`.
111    pub(crate) fn into_sets(self) -> Vec<(&'static str, ExprKind)> {
112        self.sets
113    }
114
115    /// `col = $n, col = $n`: the one renderer for a `SET` list, shared by
116    /// `UPDATE` and `ON CONFLICT DO UPDATE`.
117    pub(crate) fn render_into<D: Dialect>(&self, sink: &mut dyn Sink) {
118        for (i, (col, value)) in self.sets.iter().enumerate() {
119            if i > 0 {
120                sink.text(", ");
121            }
122            render_ident::<D>(sink, col);
123            sink.text(" = ");
124            render_expr::<D>(value, sink);
125        }
126    }
127
128    /// The `SET` list a request struct describes. This is the one fallible
129    /// step in building a statement, since a `*Update` whose every field is
130    /// untouched describes no assignment. Mixed with computed ones as
131    /// `Assignments::from_row(patch)?.and_set_to(col, expr)`.
132    pub fn from_row<R: UpdateRow<Table = T>>(row: R) -> Result<Self, NothingToSet> {
133        let mut sets: Vec<(&'static str, ExprKind)> = Vec::new();
134        for (col, value) in row.sets() {
135            push_set(&mut sets, col, ExprKind::Value(value));
136        }
137        if sets.is_empty() {
138            return Err(NothingToSet);
139        }
140        Ok(Assignments {
141            sets,
142            _marker: PhantomData,
143        })
144    }
145}
146
147/// Every field of an `*Update` was untouched, so there is nothing to
148/// assign. Returned rather than panicked because request-shaped data
149/// produces it: the caller decides whether that is a no-op or an error.
150#[derive(Debug, Clone, Copy, PartialEq, Eq)]
151pub struct NothingToSet;
152
153impl std::fmt::Display for NothingToSet {
154    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
155        f.write_str("an UPDATE must set at least one column, but every field of this `*Update` is untouched")
156    }
157}
158impl std::error::Error for NothingToSet {}
159
160pub struct UpdateSeed<D, T> {
161    _marker: PhantomData<fn() -> (D, T)>,
162}
163
164pub fn update<D, T: BaseTable>(_table: T) -> UpdateSeed<D, T> {
165    UpdateSeed {
166        _marker: PhantomData,
167    }
168}
169
170impl<D, T: Table> UpdateSeed<D, T> {
171    /// A correlated subquery over the table this statement will write.
172    /// It is available before the `SET` list, since the scope it correlates
173    /// against is the table, not the assignments.
174    pub fn correlated<S, InnerSel>(
175        &self,
176        source: S,
177        selection: InnerSel,
178    ) -> crate::select::Select<
179        D,
180        crate::scope::Cons<
181            crate::scope::TableSlot<S::Table, crate::scope::NotNull>,
182            WrittenTable<T>,
183        >,
184        InnerSel,
185        WrittenTable<T>,
186    >
187    where
188        S: crate::select::JoinSource<D>,
189    {
190        crate::select::correlated_with(source, selection)
191    }
192
193    /// `SET column = <expression>` as the statement's first assignment.
194    /// Infallible, since one assignment is one assignment. `.set_to(..)`
195    /// again for more.
196    pub fn set_to<C, V, Idxs>(self, column: Column<C>, value: V) -> Update<D, T>
197    where
198        C: ColumnKey<Table = T> + Writable,
199        V: IntoExpr,
200        V::Sql: AssignsTo<C::Sql>,
201        WrittenTable<T>: Superset<V::Req, Idxs>,
202    {
203        Update {
204            sets: Assignments::set_to(column, value),
205            wheres: Vec::new(),
206            _marker: PhantomData,
207        }
208    }
209
210    /// The statement's `SET` list. Infallible: an `Assignments` holds at
211    /// least one assignment by construction, and the empty case an
212    /// `*Update` can be lives in `Assignments::from_row`, which is where
213    /// the `?` goes.
214    pub fn set(self, sets: Assignments<T>) -> Update<D, T> {
215        Update {
216            sets,
217            wheres: Vec::new(),
218            _marker: PhantomData,
219        }
220    }
221}
222
223fn render_set_clause<D: Dialect, T: Table>(
224    sink: &mut dyn Sink,
225    sets: &Assignments<T>,
226    wheres: &[ExprKind],
227) {
228    sink.text("UPDATE ");
229    render_ident::<D>(sink, T::NAME);
230    sink.text(" SET ");
231    sets.render_into::<D>(sink);
232    render_and_list::<D>(sink, " WHERE ", wheres);
233}
234
235pub struct Update<D, T: Table> {
236    sets: Assignments<T>,
237    wheres: Vec<ExprKind>,
238    _marker: PhantomData<fn() -> (D, T)>,
239}
240
241impl<D, T: Table> Update<D, T> {
242    /// A correlated subquery over the table this statement writes. It is
243    /// the same `EXISTS` a `SELECT` builds with `Select::correlated`,
244    /// against the one-table scope a write statement has.
245    pub fn correlated<S, InnerSel>(
246        &self,
247        source: S,
248        selection: InnerSel,
249    ) -> crate::select::Select<
250        D,
251        crate::scope::Cons<
252            crate::scope::TableSlot<S::Table, crate::scope::NotNull>,
253            WrittenTable<T>,
254        >,
255        InnerSel,
256        WrittenTable<T>,
257    >
258    where
259        S: crate::select::JoinSource<D>,
260    {
261        crate::select::correlated_with(source, selection)
262    }
263
264    pub fn filter<C: Condition<D, WrittenTable<T>, Idxs>, Idxs>(mut self, cond: C) -> Self {
265        self.wheres.push(cond.into_predicate().into_kind());
266        self
267    }
268
269    /// AND-folds a runtime-length collection of discharged conditions, the
270    /// same way `Select::filter_all` does. A `PATCH` narrows its rows by
271    /// however many criteria the request carried.
272    pub fn filter_all(
273        mut self,
274        conds: impl IntoIterator<Item = Predicate<D, WrittenTable<T>>>,
275    ) -> Self {
276        self.wheres
277            .extend(conds.into_iter().map(Predicate::into_kind));
278        self
279    }
280}
281
282impl<D, T: Table> Update<D, T> {
283    /// One more assignment, appended to whatever `.set(..)` already
284    /// assigned. See `Assignments::set_to`.
285    pub fn set_to<C, V, Idxs>(mut self, column: Column<C>, value: V) -> Self
286    where
287        C: ColumnKey<Table = T> + Writable,
288        V: IntoExpr,
289        V::Sql: AssignsTo<C::Sql>,
290        WrittenTable<T>: Superset<V::Req, Idxs>,
291    {
292        self.sets = self.sets.and_set_to(column, value);
293        self
294    }
295}
296
297impl<D: Dialect, T: Table> crate::statement::private::Sealed for Update<D, T> {}
298
299impl<D: Dialect, T: Table> Statement for Update<D, T> {
300    type Dialect = D;
301    type Table = T;
302    fn render_into(&self, sink: &mut dyn Sink) {
303        render_set_clause::<D, T>(sink, &self.sets, &self.wheres);
304    }
305}