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