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::{QuerySink, 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
34/// assignments at all, so the check 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>` — the assignments a value can't say:
63    /// `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        // A column assigned twice is not a statement any database accepts,
88        // and layering a computed assignment over a request's is exactly
89        // when it happens — so the later one replaces the earlier.
90        let name = <C as crate::row::Named>::NAME;
91        self.sets.retain(|(col, _)| *col != name);
92        self.sets.push((name, value.into_expr().kind));
93        self
94    }
95}
96
97impl<T> Assignments<T> {
98    /// `col = $n, col = $n` — the one renderer for a `SET` list, shared by
99    /// `UPDATE` and `ON CONFLICT DO UPDATE`.
100    pub(crate) fn render_into<D: Dialect>(&self, sink: &mut dyn Sink) {
101        for (i, (col, value)) in self.sets.iter().enumerate() {
102            if i > 0 {
103                sink.text(", ");
104            }
105            render_ident::<D>(sink, col);
106            sink.text(" = ");
107            render_expr::<D>(value, sink);
108        }
109    }
110
111    /// The `SET` list a request struct describes — the one fallible step
112    /// in building a statement, since a `*Update` whose every field is
113    /// untouched describes no assignment. Mixed with computed ones as
114    /// `Assignments::from_row(patch)?.and_set_to(col, expr)`.
115    pub fn from_row<R: UpdateRow<Table = T>>(row: R) -> Result<Self, NothingToSet> {
116        let mut sets: Vec<(&'static str, ExprKind)> = Vec::new();
117        for (col, value) in row.sets() {
118            // Last write wins, as `and_set_to` says: a `SET` list naming one
119            // column twice is a statement no database accepts, and this is
120            // the other place the list is built.
121            sets.retain(|(name, _)| *name != col);
122            sets.push((col, ExprKind::Value(value)));
123        }
124        if sets.is_empty() {
125            return Err(NothingToSet);
126        }
127        Ok(Assignments {
128            sets,
129            _marker: PhantomData,
130        })
131    }
132}
133
134/// Every field of an `*Update` was untouched, so there is nothing to
135/// assign. Returned rather than panicked because request-shaped data
136/// produces it: the caller decides whether that is a no-op or an error.
137#[derive(Debug, Clone, Copy, PartialEq, Eq)]
138pub struct NothingToSet;
139
140impl std::fmt::Display for NothingToSet {
141    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
142        f.write_str("an UPDATE must set at least one column, but every field of this `*Update` is untouched")
143    }
144}
145impl std::error::Error for NothingToSet {}
146
147pub struct UpdateSeed<D, T> {
148    _marker: PhantomData<fn() -> (D, T)>,
149}
150
151pub fn update<D, T: BaseTable>(_table: T) -> UpdateSeed<D, T> {
152    UpdateSeed {
153        _marker: PhantomData,
154    }
155}
156
157impl<D, T: Table> UpdateSeed<D, T> {
158    /// A correlated subquery over the table this statement will write —
159    /// available before the `SET` list, since the scope it correlates
160    /// against is the table, not the assignments.
161    pub fn correlated<S, InnerSel>(
162        &self,
163        source: S,
164        selection: InnerSel,
165    ) -> crate::select::Select<
166        D,
167        crate::scope::Cons<
168            crate::scope::TableSlot<S::Table, crate::scope::NotNull>,
169            WrittenTable<T>,
170        >,
171        InnerSel,
172        WrittenTable<T>,
173    >
174    where
175        S: crate::select::JoinSource<D>,
176    {
177        crate::select::correlated_with(source, selection)
178    }
179
180    /// `SET column = <expression>` as the statement's first assignment —
181    /// infallible, since one assignment is one assignment. `.set_to(..)`
182    /// again for more.
183    pub fn set_to<C, V, Idxs>(self, column: Column<C>, value: V) -> Update<D, T>
184    where
185        C: ColumnKey<Table = T> + Writable,
186        V: IntoExpr,
187        V::Sql: AssignsTo<C::Sql>,
188        WrittenTable<T>: Superset<V::Req, Idxs>,
189    {
190        Update {
191            sets: Assignments::set_to(column, value),
192            wheres: Vec::new(),
193            _marker: PhantomData,
194        }
195    }
196
197    /// The statement's `SET` list. Infallible: an `Assignments` holds at
198    /// least one assignment by construction, and the empty case an
199    /// `*Update` can be lives in `Assignments::from_row`, which is where
200    /// the `?` goes.
201    pub fn set(self, sets: Assignments<T>) -> Update<D, T> {
202        Update {
203            sets,
204            wheres: Vec::new(),
205            _marker: PhantomData,
206        }
207    }
208}
209
210fn render_set_clause<D: Dialect, T: Table>(
211    sets: &Assignments<T>,
212    wheres: &[ExprKind],
213) -> QuerySink<D> {
214    let mut sink = QuerySink::<D>::new();
215    sink.text("UPDATE ");
216    render_ident::<D>(&mut sink, T::NAME);
217    sink.text(" SET ");
218    sets.render_into::<D>(&mut sink);
219
220    render_and_list::<D>(&mut sink, " WHERE ", wheres);
221
222    sink
223}
224
225pub struct Update<D, T: Table> {
226    sets: Assignments<T>,
227    wheres: Vec<ExprKind>,
228    _marker: PhantomData<fn() -> (D, T)>,
229}
230
231impl<D, T: Table> Update<D, T> {
232    /// A correlated subquery over the table this statement writes — the
233    /// same `EXISTS` a `SELECT` builds with `Select::correlated`, against
234    /// the one-table scope a write statement has.
235    pub fn correlated<S, InnerSel>(
236        &self,
237        source: S,
238        selection: InnerSel,
239    ) -> crate::select::Select<
240        D,
241        crate::scope::Cons<
242            crate::scope::TableSlot<S::Table, crate::scope::NotNull>,
243            WrittenTable<T>,
244        >,
245        InnerSel,
246        WrittenTable<T>,
247    >
248    where
249        S: crate::select::JoinSource<D>,
250    {
251        crate::select::correlated_with(source, selection)
252    }
253
254    pub fn filter<C: Condition<D, WrittenTable<T>, Idxs>, Idxs>(mut self, cond: C) -> Self {
255        self.wheres.push(cond.into_predicate().into_kind());
256        self
257    }
258
259    /// AND-folds a runtime-length collection of discharged conditions, the
260    /// same way `Select::filter_all` does — a `PATCH` narrows its rows by
261    /// however many criteria the request carried.
262    pub fn filter_all(
263        mut self,
264        conds: impl IntoIterator<Item = Predicate<D, WrittenTable<T>>>,
265    ) -> Self {
266        self.wheres
267            .extend(conds.into_iter().map(Predicate::into_kind));
268        self
269    }
270}
271
272impl<D, T: Table> Update<D, T> {
273    /// One more assignment, appended to whatever `.set(..)` already
274    /// assigned — see `Assignments::set_to`.
275    pub fn set_to<C, V, Idxs>(mut self, column: Column<C>, value: V) -> Self
276    where
277        C: ColumnKey<Table = T> + Writable,
278        V: IntoExpr,
279        V::Sql: AssignsTo<C::Sql>,
280        WrittenTable<T>: Superset<V::Req, Idxs>,
281    {
282        self.sets = self.sets.and_set_to(column, value);
283        self
284    }
285}
286
287impl<D: Dialect, T: Table> crate::statement::private::Sealed for Update<D, T> {}
288
289impl<D: Dialect, T: Table> Statement for Update<D, T> {
290    type Dialect = D;
291    type Table = T;
292    fn render(&self) -> QuerySink<D> {
293        render_set_clause::<D, T>(&self.sets, &self.wheres)
294    }
295}