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}