1use 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
12pub trait UpdateRow: private::Sealed {
17 type Table: Table;
18 fn sets(self) -> Vec<(&'static str, Value)>;
19}
20
21mod private {
22 pub trait Sealed {}
26}
27
28#[doc(hidden)]
29pub use private::Sealed as UpdateRowSealed;
30
31pub struct Assignments<T> {
37 sets: Vec<(&'static str, ExprKind)>,
38 _marker: PhantomData<fn() -> T>,
39}
40
41impl<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 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 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
96pub(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 pub(crate) fn into_sets(self) -> Vec<(&'static str, ExprKind)> {
112 self.sets
113 }
114
115 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 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#[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 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 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 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 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 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 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}