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