drizzle_sqlite/builder/update.rs
1//! The UPDATE builder: [`UpdateBuilder`] and its states.
2//!
3//! Start an UPDATE with [`QueryBuilder::update`](super::QueryBuilder::update).
4
5use crate::common::SQLiteSchemaType;
6use crate::traits::SQLiteTable;
7use crate::values::SQLiteValue;
8use core::marker::PhantomData;
9use drizzle_core::ToSQL;
10
11//------------------------------------------------------------------------------
12// Type State Markers
13//------------------------------------------------------------------------------
14
15pub use drizzle_core::builder::{
16 UpdateInitial, UpdateReturningSet, UpdateSetClauseSet, UpdateWhereSet,
17};
18
19//------------------------------------------------------------------------------
20// UpdateBuilder Definition
21//------------------------------------------------------------------------------
22
23/// An UPDATE query being built for `SQLite`.
24///
25/// This is [`QueryBuilder`](super::QueryBuilder) in one of the `Update*`
26/// states. Start it with [`QueryBuilder::update`](super::QueryBuilder::update).
27///
28/// # Clause order
29///
30/// 1. [`set`](Self::set) (required before the query can run).
31/// 2. `where` (required): the rows to update. `r#where(true)` updates every
32/// row.
33/// 3. Optionally [`returning`](Self::returning).
34///
35/// The WHERE condition and the RETURNING columns may only reference the
36/// table being updated; other tables do not compile.
37///
38/// # Examples
39///
40/// ```rust
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 sqlite {
49/// # pub use drizzle_sqlite::*;
50/// # #[cfg(feature = "rusqlite")]
51/// # pub mod rusqlite { pub use ::rusqlite::{Error, Result, Row, types}; }
52/// # #[cfg(feature = "libsql")]
53/// # pub mod libsql { pub use ::libsql::{Row, Value}; }
54/// # #[cfg(feature = "turso")]
55/// # pub mod turso { pub use ::turso::{Error, IntoValue, Result, Row, Value}; }
56/// # pub mod prelude {
57/// # pub use drizzle_macros::{SQLiteTable, SQLiteSchema};
58/// # pub use drizzle_sqlite::{*, attrs::*};
59/// # pub use drizzle_core::*;
60/// # }
61/// # }
62/// # }
63/// use drizzle::sqlite::prelude::*;
64/// use drizzle::core::expr::eq;
65/// use drizzle::sqlite::builder::QueryBuilder;
66///
67/// #[SQLiteTable(name = "users")]
68/// struct User {
69/// #[column(primary)]
70/// id: i32,
71/// name: String,
72/// email: Option<String>,
73/// }
74///
75/// #[derive(SQLiteSchema)]
76/// struct Schema {
77/// user: User,
78/// }
79///
80/// let builder = QueryBuilder::new::<Schema>();
81/// let Schema { user } = Schema::new();
82///
83/// // Basic UPDATE
84/// let query = builder
85/// .update(user)
86/// .set(UpdateUser::default().with_name("Alice Updated"))
87/// .r#where(eq(user.id, 1));
88/// assert_eq!(
89/// query.to_sql().sql(),
90/// r#"UPDATE "users" SET "name" = ? WHERE "users"."id" = ?"#
91/// );
92/// ```
93///
94/// Several columns at once:
95///
96/// ```rust
97/// # mod drizzle {
98/// # pub mod core { pub use drizzle_core::*; }
99/// # pub mod error { pub use drizzle_core::error::*; }
100/// # pub mod types { pub use drizzle_types::*; }
101/// # pub mod migrations { pub use drizzle_migrations::*; }
102/// # pub use drizzle_types::Dialect;
103/// # pub use drizzle_types as ddl;
104/// # pub mod sqlite {
105/// # pub use drizzle_sqlite::*;
106/// # #[cfg(feature = "rusqlite")]
107/// # pub mod rusqlite { pub use ::rusqlite::{Error, Result, Row, types}; }
108/// # #[cfg(feature = "libsql")]
109/// # pub mod libsql { pub use ::libsql::{Row, Value}; }
110/// # #[cfg(feature = "turso")]
111/// # pub mod turso { pub use ::turso::{Error, IntoValue, Result, Row, Value}; }
112/// # pub mod prelude {
113/// # pub use drizzle_macros::{SQLiteTable, SQLiteSchema};
114/// # pub use drizzle_sqlite::{*, attrs::*};
115/// # pub use drizzle_core::*;
116/// # }
117/// # }
118/// # }
119/// # use drizzle::sqlite::prelude::*;
120/// # use drizzle::core::expr::eq;
121/// # use drizzle::sqlite::builder::QueryBuilder;
122/// # #[SQLiteTable(name = "users")] struct User { #[column(primary)] id: i32, name: String, email: Option<String> }
123/// # #[derive(SQLiteSchema)] struct Schema { user: User }
124/// # let builder = QueryBuilder::new::<Schema>();
125/// # let Schema { user } = Schema::new();
126/// let query = builder
127/// .update(user)
128/// .set(UpdateUser::default()
129/// .with_name("Alice Updated")
130/// .with_email("alice.new@example.com"))
131/// .r#where(eq(user.id, 1));
132/// assert_eq!(
133/// query.to_sql().sql(),
134/// r#"UPDATE "users" SET "name" = ?, "email" = ? WHERE "users"."id" = ?"#
135/// );
136/// ```
137///
138/// With RETURNING:
139///
140/// ```rust
141/// # mod drizzle {
142/// # pub mod core { pub use drizzle_core::*; }
143/// # pub mod error { pub use drizzle_core::error::*; }
144/// # pub mod types { pub use drizzle_types::*; }
145/// # pub mod migrations { pub use drizzle_migrations::*; }
146/// # pub use drizzle_types::Dialect;
147/// # pub use drizzle_types as ddl;
148/// # pub mod sqlite {
149/// # pub use drizzle_sqlite::*;
150/// # #[cfg(feature = "rusqlite")]
151/// # pub mod rusqlite { pub use ::rusqlite::{Error, Result, Row, types}; }
152/// # #[cfg(feature = "libsql")]
153/// # pub mod libsql { pub use ::libsql::{Row, Value}; }
154/// # #[cfg(feature = "turso")]
155/// # pub mod turso { pub use ::turso::{Error, IntoValue, Result, Row, Value}; }
156/// # pub mod prelude {
157/// # pub use drizzle_macros::{SQLiteTable, SQLiteSchema};
158/// # pub use drizzle_sqlite::{*, attrs::*};
159/// # pub use drizzle_core::*;
160/// # }
161/// # }
162/// # }
163/// # use drizzle::sqlite::prelude::*;
164/// # use drizzle::core::expr::eq;
165/// # use drizzle::sqlite::builder::QueryBuilder;
166/// # #[SQLiteTable(name = "users")] struct User { #[column(primary)] id: i32, name: String, age: Option<i32> }
167/// # #[derive(SQLiteSchema)] struct Schema { user: User }
168/// # let builder = QueryBuilder::new::<Schema>();
169/// # let Schema { user } = Schema::new();
170/// let query = builder
171/// .update(user)
172/// .set(UpdateUser::default().with_name("Alice Updated"))
173/// .r#where(eq(user.id, 1))
174/// .returning((user.id, user.name));
175/// assert_eq!(
176/// query.to_sql().sql(),
177/// r#"UPDATE "users" SET "name" = ? WHERE "users"."id" = ? RETURNING "users"."id", "users"."name""#
178/// );
179/// ```
180pub type UpdateBuilder<'a, Schema, State, Table, Marker = (), Row = ()> =
181 super::QueryBuilder<'a, Schema, State, Table, Marker, Row>;
182
183type ReturningMarker<Table, Columns> = drizzle_core::Scoped<
184 <Columns as drizzle_core::IntoSelectTarget>::Marker,
185 drizzle_core::Cons<Table, drizzle_core::Nil>,
186>;
187
188type ReturningRow<Table, Columns> =
189 <<Columns as drizzle_core::IntoSelectTarget>::Marker as drizzle_core::ResolveRow<Table>>::Row;
190
191type ReturningBuilder<'a, S, T, Columns> = UpdateBuilder<
192 'a,
193 S,
194 UpdateReturningSet,
195 T,
196 ReturningMarker<T, Columns>,
197 ReturningRow<T, Columns>,
198>;
199
200//------------------------------------------------------------------------------
201// Initial State Implementation
202//------------------------------------------------------------------------------
203
204impl<'a, Schema, Table> UpdateBuilder<'a, Schema, UpdateInitial, Table>
205where
206 Table: SQLiteTable<'a>,
207{
208 /// Sets the columns to change, using the table's generated update model.
209 ///
210 /// Start from `UpdateX::default()` and call a `with_*` setter for each
211 /// column to change. Columns you do not set are left as they are.
212 ///
213 /// # Examples
214 ///
215 /// ```rust
216 /// # mod drizzle {
217 /// # pub mod core { pub use drizzle_core::*; }
218 /// # pub mod error { pub use drizzle_core::error::*; }
219 /// # pub mod types { pub use drizzle_types::*; }
220 /// # pub mod migrations { pub use drizzle_migrations::*; }
221 /// # pub use drizzle_types::Dialect;
222 /// # pub use drizzle_types as ddl;
223 /// # pub mod sqlite {
224 /// # pub use drizzle_sqlite::*;
225 /// # #[cfg(feature = "rusqlite")]
226 /// # pub mod rusqlite { pub use ::rusqlite::{Error, Result, Row, types}; }
227 /// # #[cfg(feature = "libsql")]
228 /// # pub mod libsql { pub use ::libsql::{Row, Value}; }
229 /// # #[cfg(feature = "turso")]
230 /// # pub mod turso { pub use ::turso::{Error, IntoValue, Result, Row, Value}; }
231 /// # pub mod prelude {
232 /// # pub use drizzle_macros::{SQLiteTable, SQLiteSchema};
233 /// # pub use drizzle_sqlite::{*, attrs::*};
234 /// # pub use drizzle_core::*;
235 /// # }
236 /// # }
237 /// # }
238 /// # use drizzle::sqlite::prelude::*;
239 /// # use drizzle::sqlite::builder::QueryBuilder;
240 /// # use drizzle::core::{ToSQL, expr::{eq, and}};
241 /// # #[SQLiteTable(name = "users")] struct User { #[column(primary)] id: i32, name: String, email: Option<String> }
242 /// # #[derive(SQLiteSchema)] struct Schema { user: User }
243 /// # let builder = QueryBuilder::new::<Schema>();
244 /// # let Schema { user } = Schema::new();
245 /// // Update single column
246 /// let query = builder
247 /// .update(user)
248 /// .set(UpdateUser::default().with_name("New Name"));
249 /// assert_eq!(query.to_sql().sql(), r#"UPDATE "users" SET "name" = ?"#);
250 ///
251 /// // Update multiple columns
252 /// let query = builder
253 /// .update(user)
254 /// .set(UpdateUser::default().with_name("New Name").with_email("new@example.com"));
255 /// assert_eq!(query.to_sql().sql(), r#"UPDATE "users" SET "name" = ?, "email" = ?"#);
256 /// ```
257 #[inline]
258 pub fn set(
259 self,
260 values: Table::Update,
261 ) -> UpdateBuilder<'a, Schema, UpdateSetClauseSet, Table> {
262 let sql = crate::helpers::set::<Table, SQLiteSchemaType, SQLiteValue<'a>>(&values);
263 drop(values);
264 UpdateBuilder {
265 sql: self.sql.append(sql),
266 schema: PhantomData,
267 state: PhantomData,
268 table: PhantomData,
269 marker: PhantomData,
270 row: PhantomData,
271 grouped: PhantomData,
272 }
273 }
274}
275
276//------------------------------------------------------------------------------
277// Post-SET Implementation
278//------------------------------------------------------------------------------
279
280impl<'a, S, T> UpdateBuilder<'a, S, UpdateSetClauseSet, T> {
281 /// Adds a WHERE clause that picks the rows to update.
282 ///
283 /// An UPDATE runs only with a WHERE clause, so a forgotten condition does
284 /// not rewrite the whole table; `r#where(true)` updates every row. The
285 /// condition must be a boolean expression over the updated table's
286 /// columns.
287 ///
288 /// # Examples
289 ///
290 /// ```rust
291 /// # mod drizzle {
292 /// # pub mod core { pub use drizzle_core::*; }
293 /// # pub mod error { pub use drizzle_core::error::*; }
294 /// # pub mod types { pub use drizzle_types::*; }
295 /// # pub mod migrations { pub use drizzle_migrations::*; }
296 /// # pub use drizzle_types::Dialect;
297 /// # pub use drizzle_types as ddl;
298 /// # pub mod sqlite {
299 /// # pub use drizzle_sqlite::*;
300 /// # #[cfg(feature = "rusqlite")]
301 /// # pub mod rusqlite { pub use ::rusqlite::{Error, Result, Row, types}; }
302 /// # #[cfg(feature = "libsql")]
303 /// # pub mod libsql { pub use ::libsql::{Row, Value}; }
304 /// # #[cfg(feature = "turso")]
305 /// # pub mod turso { pub use ::turso::{Error, IntoValue, Result, Row, Value}; }
306 /// # pub mod prelude {
307 /// # pub use drizzle_macros::{SQLiteTable, SQLiteSchema};
308 /// # pub use drizzle_sqlite::{*, attrs::*};
309 /// # pub use drizzle_core::*;
310 /// # }
311 /// # }
312 /// # }
313 /// # use drizzle::sqlite::prelude::*;
314 /// # use drizzle::core::expr::{eq, gt, and};
315 /// # use drizzle::sqlite::builder::QueryBuilder;
316 /// # #[SQLiteTable(name = "users")] struct User { #[column(primary)] id: i32, name: String, age: Option<i32> }
317 /// # #[derive(SQLiteSchema)] struct Schema { user: User }
318 /// # let builder = QueryBuilder::new::<Schema>();
319 /// # let Schema { user } = Schema::new();
320 /// // Update specific row by ID
321 /// let query = builder
322 /// .update(user)
323 /// .set(UpdateUser::default().with_name("Updated Name"))
324 /// .r#where(eq(user.id, 1));
325 /// assert_eq!(
326 /// query.to_sql().sql(),
327 /// r#"UPDATE "users" SET "name" = ? WHERE "users"."id" = ?"#
328 /// );
329 ///
330 /// // Update multiple rows with complex condition
331 /// let query = builder
332 /// .update(user)
333 /// .set(UpdateUser::default().with_name("Updated"))
334 /// .r#where(and(gt(user.id, 10), eq(user.age, 25)));
335 /// ```
336 #[inline]
337 pub fn r#where<E, ScopeProof>(self, condition: E) -> UpdateBuilder<'a, S, UpdateWhereSet, T>
338 where
339 E: drizzle_core::expr::ExprSources,
340 E::Sources:
341 drizzle_core::scope::SourcesIn<drizzle_core::Cons<T, drizzle_core::Nil>, ScopeProof>,
342 E: drizzle_core::expr::Expr<'a, SQLiteValue<'a>>,
343 E::SQLType: drizzle_core::types::BooleanLike,
344 {
345 let where_sql = crate::helpers::r#where(condition);
346 UpdateBuilder {
347 sql: self.sql.append(where_sql),
348 schema: PhantomData,
349 state: PhantomData,
350 table: PhantomData,
351 marker: PhantomData,
352 row: PhantomData,
353 grouped: PhantomData,
354 }
355 }
356}
357
358//------------------------------------------------------------------------------
359// Post-WHERE Implementation
360//------------------------------------------------------------------------------
361
362impl<'a, S, T> UpdateBuilder<'a, S, UpdateWhereSet, T> {
363 /// Adds a RETURNING clause after WHERE. See
364 /// [`returning`](UpdateBuilder::returning).
365 #[inline]
366 pub fn returning<Columns, ScopeProof>(
367 self,
368 columns: Columns,
369 ) -> ReturningBuilder<'a, S, T, Columns>
370 where
371 Columns: drizzle_core::expr::ExprSources,
372 Columns::Sources:
373 drizzle_core::scope::SourcesIn<drizzle_core::Cons<T, drizzle_core::Nil>, ScopeProof>,
374 Columns: ToSQL<'a, SQLiteValue<'a>> + drizzle_core::IntoSelectTarget,
375 Columns::Marker: drizzle_core::ResolveRow<T>,
376 {
377 let returning_sql = crate::helpers::returning(columns);
378 UpdateBuilder {
379 sql: self.sql.append(returning_sql),
380 schema: PhantomData,
381 state: PhantomData,
382 table: PhantomData,
383 marker: PhantomData,
384 row: PhantomData,
385 grouped: PhantomData,
386 }
387 }
388}