Skip to main content

drizzle_postgres/builder/
update.rs

1//! `UPDATE` builder states and clause methods.
2//!
3//! [`UpdateBuilder`] is the builder returned by `QueryBuilder::update`. Its
4//! state parameter only allows steps in SQL order: `SET`, then `FROM`,
5//! `WHERE` and `RETURNING`. Column checks on `WHERE` and `RETURNING` happen
6//! at the method call: they may only read the updated table and the `FROM`
7//! source.
8
9use crate::common::PostgresSchemaType;
10use crate::values::PostgresValue;
11use core::marker::PhantomData;
12use drizzle_core::{SQLTable, ToSQL};
13
14// Import the ExecutableState trait
15use super::ExecutableState;
16
17//------------------------------------------------------------------------------
18// Type State Markers
19//------------------------------------------------------------------------------
20
21pub use drizzle_core::builder::{
22    UpdateInitial, UpdateReturningSet, UpdateSetClauseSet, UpdateWhereSet,
23};
24
25/// Builder state after `UPDATE ... SET ... FROM source`.
26#[derive(Debug, Clone, Copy, Default)]
27pub struct UpdateFromSet;
28
29impl ExecutableState for UpdateFromSet {}
30
31//------------------------------------------------------------------------------
32// UpdateBuilder Definition
33//------------------------------------------------------------------------------
34
35/// A `PostgreSQL` `UPDATE` being built: a [`QueryBuilder`](super::QueryBuilder)
36/// in one of the `Update*` states.
37///
38/// Start with `.set(update_model)`, then optionally add `FROM`, `WHERE` and
39/// `RETURNING`, in that order. Without `WHERE`, every row is updated.
40///
41/// # Examples
42///
43/// ```rust
44/// # extern crate self as drizzle;
45/// # mod _drizzle {
46/// #     pub mod core { pub use drizzle_core::*; }
47/// #     pub mod error { pub use drizzle_core::error::*; }
48/// #     pub mod types { pub use drizzle_types::*; }
49/// #     pub mod migrations { pub use drizzle_migrations::*; }
50/// #     pub use drizzle_types::Dialect;
51/// #     pub use drizzle_types as ddl;
52/// #     pub mod postgres {
53/// #         pub mod values { pub use drizzle_postgres::values::*; }
54/// #         pub mod traits { pub use drizzle_postgres::traits::*; }
55/// #         pub mod common { pub use drizzle_postgres::common::*; }
56/// #         pub mod attrs { pub use drizzle_postgres::attrs::*; }
57/// #         pub mod builder { pub use drizzle_postgres::builder::*; }
58/// #         pub mod helpers { pub use drizzle_postgres::helpers::*; }
59/// #         pub mod expr { pub use drizzle_postgres::expr::*; }
60/// #         pub mod types { pub use drizzle_postgres::types::*; }
61/// #         #[cfg(feature = "aws-data-api")]
62/// #         pub mod aws_data_api { pub use drizzle_postgres::aws_data_api::*; }
63/// #         pub struct Row;
64/// #         impl Row {
65/// #             pub fn get<'a, I, T>(&'a self, _: I) -> T { unimplemented!() }
66/// #             pub fn try_get<'a, I, T>(&'a self, _: I) -> Result<T, Box<dyn std::error::Error + Sync + Send>> { unimplemented!() }
67/// #         }
68/// #         pub mod prelude {
69/// #             pub use drizzle_macros::{PostgresTable, PostgresSchema, PostgresIndex};
70/// #             pub use drizzle_postgres::attrs::*;
71/// #             pub use drizzle_postgres::common::PostgresSchemaType;
72/// #             pub use drizzle_postgres::traits::{PostgresColumn, PostgresTable};
73/// #             pub use drizzle_postgres::values::{PostgresInsertValue, PostgresUpdateValue, PostgresValue};
74/// #             pub use drizzle_core::*;
75/// #         }
76/// #     }
77/// # }
78/// # pub use _drizzle::*;
79/// # pub use const_format;
80/// # fn main() {
81/// # use drizzle::postgres::prelude::*;
82/// # use drizzle::postgres::builder::QueryBuilder;
83/// # #[PostgresTable(name = "users")]
84/// # struct User {
85/// #     #[column(serial, primary)]
86/// #     id: i32,
87/// #     name: String,
88/// #     email: Option<String>,
89/// # }
90/// # #[PostgresTable(name = "posts")]
91/// # struct Post {
92/// #     #[column(serial, primary)]
93/// #     id: i32,
94/// #     #[column(references = User::id)]
95/// #     author_id: i32,
96/// #     title: String,
97/// # }
98/// # #[derive(PostgresSchema)]
99/// # struct Schema {
100/// #     user: User,
101/// #     post: Post,
102/// # }
103/// # let db = QueryBuilder::new::<Schema>();
104/// # let Schema { user, post } = Schema::new();
105/// use drizzle::core::expr::eq;
106///
107/// let query = db
108///     .update(user)
109///     .set(UpdateUser::default().with_name("Bob"))
110///     .r#where(eq(user.id, 1))
111///     .returning(user.id);
112/// assert_eq!(
113///     query.to_sql().sql(),
114///     r#"UPDATE "users" SET "name" = $1 WHERE "users"."id" = $2 RETURNING "users"."id""#
115/// );
116/// # }
117/// ```
118pub type UpdateBuilder<'a, Schema, State, Table, Marker = (), Row = ()> =
119    super::QueryBuilder<'a, Schema, State, Table, Marker, Row>;
120
121type ReturningMarker<Table, Columns> = drizzle_core::Scoped<
122    <Columns as drizzle_core::IntoSelectTarget>::Marker,
123    drizzle_core::Cons<Table, drizzle_core::Nil>,
124>;
125
126type ReturningRow<Table, Columns> =
127    <<Columns as drizzle_core::IntoSelectTarget>::Marker as drizzle_core::ResolveRow<Table>>::Row;
128
129type ReturningBuilder<'a, S, T, Columns> = UpdateBuilder<
130    'a,
131    S,
132    UpdateReturningSet,
133    T,
134    ReturningMarker<T, Columns>,
135    ReturningRow<T, Columns>,
136>;
137
138//------------------------------------------------------------------------------
139// Initial State Implementation
140//------------------------------------------------------------------------------
141
142impl<'a, Schema, Table> UpdateBuilder<'a, Schema, UpdateInitial, Table>
143where
144    Table: SQLTable<'a, PostgresSchemaType, PostgresValue<'a>>,
145{
146    /// Sets the new column values from the table's `Update*` model.
147    ///
148    /// Only the fields set on the model (with `.with_*`) are written.
149    #[inline]
150    pub fn set(
151        self,
152        values: Table::Update,
153    ) -> UpdateBuilder<'a, Schema, UpdateSetClauseSet, Table> {
154        let sql = crate::helpers::set::<Table, PostgresSchemaType, PostgresValue<'a>>(&values);
155        drop(values);
156        UpdateBuilder {
157            sql: self.sql.append(sql),
158            schema: PhantomData,
159            state: PhantomData,
160            table: PhantomData,
161            marker: PhantomData,
162            row: PhantomData,
163            grouped: PhantomData,
164        }
165    }
166}
167
168//------------------------------------------------------------------------------
169// Post-SET Implementation
170//------------------------------------------------------------------------------
171
172impl<'a, S, T> UpdateBuilder<'a, S, UpdateSetClauseSet, T> {
173    /// Adds `FROM source`, making another table's columns available to
174    /// `WHERE` and `RETURNING`.
175    ///
176    /// # Examples
177    ///
178    /// ```rust
179    /// # extern crate self as drizzle;
180    /// # mod _drizzle {
181    /// #     pub mod core { pub use drizzle_core::*; }
182    /// #     pub mod error { pub use drizzle_core::error::*; }
183    /// #     pub mod types { pub use drizzle_types::*; }
184    /// #     pub mod migrations { pub use drizzle_migrations::*; }
185    /// #     pub use drizzle_types::Dialect;
186    /// #     pub use drizzle_types as ddl;
187    /// #     pub mod postgres {
188    /// #         pub mod values { pub use drizzle_postgres::values::*; }
189    /// #         pub mod traits { pub use drizzle_postgres::traits::*; }
190    /// #         pub mod common { pub use drizzle_postgres::common::*; }
191    /// #         pub mod attrs { pub use drizzle_postgres::attrs::*; }
192    /// #         pub mod builder { pub use drizzle_postgres::builder::*; }
193    /// #         pub mod helpers { pub use drizzle_postgres::helpers::*; }
194    /// #         pub mod expr { pub use drizzle_postgres::expr::*; }
195    /// #         pub mod types { pub use drizzle_postgres::types::*; }
196    /// #         #[cfg(feature = "aws-data-api")]
197    /// #         pub mod aws_data_api { pub use drizzle_postgres::aws_data_api::*; }
198    /// #         pub struct Row;
199    /// #         impl Row {
200    /// #             pub fn get<'a, I, T>(&'a self, _: I) -> T { unimplemented!() }
201    /// #             pub fn try_get<'a, I, T>(&'a self, _: I) -> Result<T, Box<dyn std::error::Error + Sync + Send>> { unimplemented!() }
202    /// #         }
203    /// #         pub mod prelude {
204    /// #             pub use drizzle_macros::{PostgresTable, PostgresSchema, PostgresIndex};
205    /// #             pub use drizzle_postgres::attrs::*;
206    /// #             pub use drizzle_postgres::common::PostgresSchemaType;
207    /// #             pub use drizzle_postgres::traits::{PostgresColumn, PostgresTable};
208    /// #             pub use drizzle_postgres::values::{PostgresInsertValue, PostgresUpdateValue, PostgresValue};
209    /// #             pub use drizzle_core::*;
210    /// #         }
211    /// #     }
212    /// # }
213    /// # pub use _drizzle::*;
214    /// # pub use const_format;
215    /// # fn main() {
216    /// # use drizzle::postgres::prelude::*;
217    /// # use drizzle::postgres::builder::QueryBuilder;
218    /// # #[PostgresTable(name = "users")]
219    /// # struct User {
220    /// #     #[column(serial, primary)]
221    /// #     id: i32,
222    /// #     name: String,
223    /// #     email: Option<String>,
224    /// # }
225    /// # #[PostgresTable(name = "posts")]
226    /// # struct Post {
227    /// #     #[column(serial, primary)]
228    /// #     id: i32,
229    /// #     #[column(references = User::id)]
230    /// #     author_id: i32,
231    /// #     title: String,
232    /// # }
233    /// # #[derive(PostgresSchema)]
234    /// # struct Schema {
235    /// #     user: User,
236    /// #     post: Post,
237    /// # }
238    /// # let db = QueryBuilder::new::<Schema>();
239    /// # let Schema { user, post } = Schema::new();
240    /// use drizzle::core::expr::eq;
241    ///
242    /// let query = db
243    ///     .update(user)
244    ///     .set(UpdateUser::default().with_name("Author"))
245    ///     .from(post)
246    ///     .r#where(eq(post.author_id, user.id));
247    /// assert_eq!(
248    ///     query.to_sql().sql(),
249    ///     r#"UPDATE "users" SET "name" = $1 FROM "posts" WHERE "posts"."author_id" = "users"."id""#
250    /// );
251    /// # }
252    /// ```
253    #[inline]
254    pub fn from<F>(
255        self,
256        source: F,
257    ) -> UpdateBuilder<'a, S, UpdateFromSet, T, drizzle_core::Cons<F, drizzle_core::Nil>>
258    where
259        F: ToSQL<'a, PostgresValue<'a>> + drizzle_core::ScopeEntry,
260    {
261        let from_sql = crate::helpers::from(source);
262        UpdateBuilder {
263            sql: self.sql.append(from_sql),
264            schema: PhantomData,
265            state: PhantomData,
266            table: PhantomData,
267            marker: PhantomData,
268            row: PhantomData,
269            grouped: PhantomData,
270        }
271    }
272
273    /// Adds a `WHERE` condition; only matching rows are updated.
274    ///
275    /// The condition may only use columns of the updated table; to use other
276    /// tables, add [`from`](Self::from) first.
277    #[inline]
278    pub fn r#where<E, ScopeProof>(self, condition: E) -> UpdateBuilder<'a, S, UpdateWhereSet, T>
279    where
280        E: drizzle_core::expr::ExprSources,
281        E::Sources:
282            drizzle_core::scope::SourcesIn<drizzle_core::Cons<T, drizzle_core::Nil>, ScopeProof>,
283        E: drizzle_core::expr::Expr<'a, PostgresValue<'a>>,
284        E::SQLType: drizzle_core::types::BooleanLike,
285    {
286        let where_sql = crate::helpers::r#where(condition);
287        UpdateBuilder {
288            sql: self.sql.append(where_sql),
289            schema: PhantomData,
290            state: PhantomData,
291            table: PhantomData,
292            marker: PhantomData,
293            row: PhantomData,
294            grouped: PhantomData,
295        }
296    }
297
298    /// Adds `RETURNING columns`, so the statement returns the updated rows.
299    #[inline]
300    pub fn returning<Columns, ScopeProof>(
301        self,
302        columns: Columns,
303    ) -> ReturningBuilder<'a, S, T, Columns>
304    where
305        Columns: drizzle_core::expr::ExprSources,
306        Columns::Sources:
307            drizzle_core::scope::SourcesIn<drizzle_core::Cons<T, drizzle_core::Nil>, ScopeProof>,
308        Columns: ToSQL<'a, PostgresValue<'a>> + drizzle_core::IntoSelectTarget,
309        Columns::Marker: drizzle_core::ResolveRow<T>,
310    {
311        let returning_sql = crate::helpers::returning(columns);
312        UpdateBuilder {
313            sql: self.sql.append(returning_sql),
314            schema: PhantomData,
315            state: PhantomData,
316            table: PhantomData,
317            marker: PhantomData,
318            row: PhantomData,
319            grouped: PhantomData,
320        }
321    }
322}
323
324//------------------------------------------------------------------------------
325// Post-FROM Implementation
326//------------------------------------------------------------------------------
327
328impl<'a, S, T, M> UpdateBuilder<'a, S, UpdateFromSet, T, M> {
329    /// Adds a `WHERE` condition, which may use the updated table and the `FROM` source.
330    #[inline]
331    pub fn r#where<E, ScopeProof>(self, condition: E) -> UpdateBuilder<'a, S, UpdateWhereSet, T, M>
332    where
333        E: drizzle_core::expr::ExprSources,
334        E::Sources: drizzle_core::scope::SourcesIn<drizzle_core::Cons<T, M>, ScopeProof>,
335        E: drizzle_core::expr::Expr<'a, PostgresValue<'a>>,
336        E::SQLType: drizzle_core::types::BooleanLike,
337    {
338        let where_sql = crate::helpers::r#where(condition);
339        UpdateBuilder {
340            sql: self.sql.append(where_sql),
341            schema: PhantomData,
342            state: PhantomData,
343            table: PhantomData,
344            marker: PhantomData,
345            row: PhantomData,
346            grouped: PhantomData,
347        }
348    }
349
350    /// Adds `RETURNING columns` after `FROM`.
351    #[inline]
352    pub fn returning<Columns, ScopeProof>(
353        self,
354        columns: Columns,
355    ) -> ReturningBuilder<'a, S, T, Columns>
356    where
357        Columns: drizzle_core::expr::ExprSources,
358        Columns::Sources: drizzle_core::scope::SourcesIn<drizzle_core::Cons<T, M>, ScopeProof>,
359        Columns: ToSQL<'a, PostgresValue<'a>> + drizzle_core::IntoSelectTarget,
360        Columns::Marker: drizzle_core::ResolveRow<T>,
361    {
362        let returning_sql = crate::helpers::returning(columns);
363        UpdateBuilder {
364            sql: self.sql.append(returning_sql),
365            schema: PhantomData,
366            state: PhantomData,
367            table: PhantomData,
368            marker: PhantomData,
369            row: PhantomData,
370            grouped: PhantomData,
371        }
372    }
373}
374
375//------------------------------------------------------------------------------
376// Post-WHERE Implementation
377//------------------------------------------------------------------------------
378
379impl<'a, S, T, M> UpdateBuilder<'a, S, UpdateWhereSet, T, M> {
380    /// Adds `RETURNING columns` after `WHERE`.
381    #[inline]
382    pub fn returning<Columns, ScopeProof>(
383        self,
384        columns: Columns,
385    ) -> ReturningBuilder<'a, S, T, Columns>
386    where
387        Columns: drizzle_core::expr::ExprSources,
388        Columns::Sources: drizzle_core::scope::SourcesIn<drizzle_core::Cons<T, M>, ScopeProof>,
389        Columns: ToSQL<'a, PostgresValue<'a>> + drizzle_core::IntoSelectTarget,
390        Columns::Marker: drizzle_core::ResolveRow<T>,
391    {
392        let returning_sql = crate::helpers::returning(columns);
393        UpdateBuilder {
394            sql: self.sql.append(returning_sql),
395            schema: PhantomData,
396            state: PhantomData,
397            table: PhantomData,
398            marker: PhantomData,
399            row: PhantomData,
400            grouped: PhantomData,
401        }
402    }
403}
404
405#[cfg(test)]
406mod tests {
407    use super::*;
408    use drizzle_core::{SQL, ToSQL};
409
410    #[test]
411    fn test_update_builder_creation() {
412        let builder = UpdateBuilder::<(), UpdateInitial, ()> {
413            sql: SQL::raw("UPDATE test"),
414            schema: PhantomData,
415            state: PhantomData,
416            table: PhantomData,
417            marker: PhantomData,
418            row: PhantomData,
419            grouped: PhantomData,
420        };
421
422        assert_eq!(builder.to_sql().sql(), "UPDATE test");
423    }
424}