Skip to main content

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}