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