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. `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}