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}