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}