Skip to main content

drizzle_sqlite/builder/
update.rs

1use crate::common::SQLiteSchemaType;
2use crate::traits::SQLiteTable;
3use crate::values::SQLiteValue;
4use core::marker::PhantomData;
5use drizzle_core::ToSQL;
6
7//------------------------------------------------------------------------------
8// Type State Markers
9//------------------------------------------------------------------------------
10
11pub use drizzle_core::builder::{
12    UpdateInitial, UpdateReturningSet, UpdateSetClauseSet, UpdateWhereSet,
13};
14
15//------------------------------------------------------------------------------
16// UpdateBuilder Definition
17//------------------------------------------------------------------------------
18
19/// Builds an UPDATE query specifically for `SQLite`.
20///
21/// `UpdateBuilder` provides a type-safe, fluent API for constructing UPDATE statements
22/// with support for conditional updates, returning clauses, and precise column targeting.
23///
24/// ## Type Parameters
25///
26/// - `Schema`: The database schema type, ensuring only valid tables can be referenced
27/// - `State`: The current builder state, enforcing proper query construction order
28/// - `Table`: The table being updated
29///
30/// ## Query Building Flow
31///
32/// 1. Start with `QueryBuilder::update(table)` to specify the target table
33/// 2. Add `set()` to specify which columns to update and their new values
34/// 3. Optionally add `where()` to limit which rows are updated
35/// 4. Optionally add `returning()` to get updated values back
36///
37/// ## Basic Usage
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/// ## Advanced Updates
94///
95/// ### Multiple Column Updates
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/// ```
133///
134/// ### UPDATE with RETURNING
135/// ```rust
136/// # mod drizzle {
137/// #     pub mod core { pub use drizzle_core::*; }
138/// #     pub mod error { pub use drizzle_core::error::*; }
139/// #     pub mod types { pub use drizzle_types::*; }
140/// #     pub mod migrations { pub use drizzle_migrations::*; }
141/// #     pub use drizzle_types::Dialect;
142/// #     pub use drizzle_types as ddl;
143/// #     pub mod sqlite {
144/// #         pub use drizzle_sqlite::*;
145/// #         #[cfg(feature = "rusqlite")]
146/// #         pub mod rusqlite { pub use ::rusqlite::{Error, Result, Row, types}; }
147/// #         #[cfg(feature = "libsql")]
148/// #         pub mod libsql { pub use ::libsql::{Row, Value}; }
149/// #         #[cfg(feature = "turso")]
150/// #         pub mod turso { pub use ::turso::{Error, IntoValue, Result, Row, Value}; }
151/// #         pub mod prelude {
152/// #             pub use drizzle_macros::{SQLiteTable, SQLiteSchema};
153/// #             pub use drizzle_sqlite::{*, attrs::*};
154/// #             pub use drizzle_core::*;
155/// #         }
156/// #     }
157/// # }
158/// # use drizzle::sqlite::prelude::*;
159/// # use drizzle::core::expr::eq;
160/// # use drizzle::sqlite::builder::QueryBuilder;
161/// # #[SQLiteTable(name = "users")] struct User { #[column(primary)] id: i32, name: String, age: Option<i32> }
162/// # #[derive(SQLiteSchema)] struct Schema { user: User }
163/// # let builder = QueryBuilder::new::<Schema>();
164/// # let Schema { user } = Schema::new();
165/// let query = builder
166///     .update(user)
167///     .set(UpdateUser::default().with_name("Alice Updated"))
168///     .r#where(eq(user.id, 1))
169///     .returning((user.id, user.name));
170/// ```
171pub type UpdateBuilder<'a, Schema, State, Table, Marker = (), Row = ()> =
172    super::QueryBuilder<'a, Schema, State, Table, Marker, Row>;
173
174type ReturningMarker<Table, Columns> = drizzle_core::Scoped<
175    <Columns as drizzle_core::IntoSelectTarget>::Marker,
176    drizzle_core::Cons<Table, drizzle_core::Nil>,
177>;
178
179type ReturningRow<Table, Columns> =
180    <<Columns as drizzle_core::IntoSelectTarget>::Marker as drizzle_core::ResolveRow<Table>>::Row;
181
182type ReturningBuilder<'a, S, T, Columns> = UpdateBuilder<
183    'a,
184    S,
185    UpdateReturningSet,
186    T,
187    ReturningMarker<T, Columns>,
188    ReturningRow<T, Columns>,
189>;
190
191//------------------------------------------------------------------------------
192// Initial State Implementation
193//------------------------------------------------------------------------------
194
195impl<'a, Schema, Table> UpdateBuilder<'a, Schema, UpdateInitial, Table>
196where
197    Table: SQLiteTable<'a>,
198{
199    /// Specifies which columns to update and their new values.
200    ///
201    /// This method accepts update expressions that specify which columns should
202    /// be modified. You can update single or multiple columns using the generated
203    /// update model's `with_*` setters.
204    ///
205    /// # Examples
206    ///
207    /// ```rust
208    /// # mod drizzle {
209    /// #     pub mod core { pub use drizzle_core::*; }
210    /// #     pub mod error { pub use drizzle_core::error::*; }
211    /// #     pub mod types { pub use drizzle_types::*; }
212    /// #     pub mod migrations { pub use drizzle_migrations::*; }
213    /// #     pub use drizzle_types::Dialect;
214    /// #     pub use drizzle_types as ddl;
215    /// #     pub mod sqlite {
216    /// #         pub use drizzle_sqlite::*;
217    /// #         #[cfg(feature = "rusqlite")]
218    /// #         pub mod rusqlite { pub use ::rusqlite::{Error, Result, Row, types}; }
219    /// #         #[cfg(feature = "libsql")]
220    /// #         pub mod libsql { pub use ::libsql::{Row, Value}; }
221    /// #         #[cfg(feature = "turso")]
222    /// #         pub mod turso { pub use ::turso::{Error, IntoValue, Result, Row, Value}; }
223    /// #         pub mod prelude {
224    /// #             pub use drizzle_macros::{SQLiteTable, SQLiteSchema};
225    /// #             pub use drizzle_sqlite::{*, attrs::*};
226    /// #             pub use drizzle_core::*;
227    /// #         }
228    /// #     }
229    /// # }
230    /// # use drizzle::sqlite::prelude::*;
231    /// # use drizzle::sqlite::builder::QueryBuilder;
232    /// # use drizzle::core::{ToSQL, expr::{eq, and}};
233    /// # #[SQLiteTable(name = "users")] struct User { #[column(primary)] id: i32, name: String, email: Option<String> }
234    /// # #[derive(SQLiteSchema)] struct Schema { user: User }
235    /// # let builder = QueryBuilder::new::<Schema>();
236    /// # let Schema { user } = Schema::new();
237    /// // Update single column
238    /// let query = builder
239    ///     .update(user)
240    ///     .set(UpdateUser::default().with_name("New Name"));
241    /// assert_eq!(query.to_sql().sql(), r#"UPDATE "users" SET "name" = ?"#);
242    ///
243    /// // Update multiple columns
244    /// let query = builder
245    ///     .update(user)
246    ///     .set(UpdateUser::default().with_name("New Name").with_email("new@example.com"));
247    /// ```
248    #[inline]
249    pub fn set(
250        self,
251        values: Table::Update,
252    ) -> UpdateBuilder<'a, Schema, UpdateSetClauseSet, Table> {
253        let sql = crate::helpers::set::<Table, SQLiteSchemaType, SQLiteValue<'a>>(&values);
254        drop(values);
255        UpdateBuilder {
256            sql: self.sql.append(sql),
257            schema: PhantomData,
258            state: PhantomData,
259            table: PhantomData,
260            marker: PhantomData,
261            row: PhantomData,
262            grouped: PhantomData,
263        }
264    }
265}
266
267//------------------------------------------------------------------------------
268// Post-SET Implementation
269//------------------------------------------------------------------------------
270
271impl<'a, S, T> UpdateBuilder<'a, S, UpdateSetClauseSet, T> {
272    /// Adds a WHERE clause to specify which rows to update.
273    ///
274    /// Without a WHERE clause, all rows in the table would be updated. This method
275    /// allows you to specify conditions to limit which rows are affected by the update.
276    ///
277    /// # Examples
278    ///
279    /// ```rust
280    /// # mod drizzle {
281    /// #     pub mod core { pub use drizzle_core::*; }
282    /// #     pub mod error { pub use drizzle_core::error::*; }
283    /// #     pub mod types { pub use drizzle_types::*; }
284    /// #     pub mod migrations { pub use drizzle_migrations::*; }
285    /// #     pub use drizzle_types::Dialect;
286    /// #     pub use drizzle_types as ddl;
287    /// #     pub mod sqlite {
288    /// #         pub use drizzle_sqlite::*;
289    /// #         #[cfg(feature = "rusqlite")]
290    /// #         pub mod rusqlite { pub use ::rusqlite::{Error, Result, Row, types}; }
291    /// #         #[cfg(feature = "libsql")]
292    /// #         pub mod libsql { pub use ::libsql::{Row, Value}; }
293    /// #         #[cfg(feature = "turso")]
294    /// #         pub mod turso { pub use ::turso::{Error, IntoValue, Result, Row, Value}; }
295    /// #         pub mod prelude {
296    /// #             pub use drizzle_macros::{SQLiteTable, SQLiteSchema};
297    /// #             pub use drizzle_sqlite::{*, attrs::*};
298    /// #             pub use drizzle_core::*;
299    /// #         }
300    /// #     }
301    /// # }
302    /// # use drizzle::sqlite::prelude::*;
303    /// # use drizzle::core::expr::{eq, gt, and};
304    /// # use drizzle::sqlite::builder::QueryBuilder;
305    /// # #[SQLiteTable(name = "users")] struct User { #[column(primary)] id: i32, name: String, age: Option<i32> }
306    /// # #[derive(SQLiteSchema)] struct Schema { user: User }
307    /// # let builder = QueryBuilder::new::<Schema>();
308    /// # let Schema { user } = Schema::new();
309    /// // Update specific row by ID
310    /// let query = builder
311    ///     .update(user)
312    ///     .set(UpdateUser::default().with_name("Updated Name"))
313    ///     .r#where(eq(user.id, 1));
314    /// assert_eq!(
315    ///     query.to_sql().sql(),
316    ///     r#"UPDATE "users" SET "name" = ? WHERE "users"."id" = ?"#
317    /// );
318    ///
319    /// // Update multiple rows with complex condition
320    /// let query = builder
321    ///     .update(user)
322    ///     .set(UpdateUser::default().with_name("Updated"))
323    ///     .r#where(and(gt(user.id, 10), eq(user.age, 25)));
324    /// ```
325    #[inline]
326    pub fn r#where<E>(self, condition: E) -> UpdateBuilder<'a, S, UpdateWhereSet, T>
327    where
328        E: drizzle_core::expr::Expr<'a, SQLiteValue<'a>>,
329        E::SQLType: drizzle_core::types::BooleanLike,
330    {
331        let where_sql = crate::helpers::r#where(condition);
332        UpdateBuilder {
333            sql: self.sql.append(where_sql),
334            schema: PhantomData,
335            state: PhantomData,
336            table: PhantomData,
337            marker: PhantomData,
338            row: PhantomData,
339            grouped: PhantomData,
340        }
341    }
342
343    /// Adds a RETURNING clause and transitions to the `ReturningSet` state
344    #[inline]
345    pub fn returning<Columns>(self, columns: Columns) -> ReturningBuilder<'a, S, T, Columns>
346    where
347        Columns: ToSQL<'a, SQLiteValue<'a>> + drizzle_core::IntoSelectTarget,
348        Columns::Marker: drizzle_core::ResolveRow<T>,
349    {
350        let returning_sql = crate::helpers::returning(columns);
351        UpdateBuilder {
352            sql: self.sql.append(returning_sql),
353            schema: PhantomData,
354            state: PhantomData,
355            table: PhantomData,
356            marker: PhantomData,
357            row: PhantomData,
358            grouped: PhantomData,
359        }
360    }
361}
362
363//------------------------------------------------------------------------------
364// Post-WHERE Implementation
365//------------------------------------------------------------------------------
366
367impl<'a, S, T> UpdateBuilder<'a, S, UpdateWhereSet, T> {
368    /// Adds a RETURNING clause after WHERE
369    #[inline]
370    pub fn returning<Columns>(self, columns: Columns) -> ReturningBuilder<'a, S, T, Columns>
371    where
372        Columns: ToSQL<'a, SQLiteValue<'a>> + drizzle_core::IntoSelectTarget,
373        Columns::Marker: drizzle_core::ResolveRow<T>,
374    {
375        let returning_sql = crate::helpers::returning(columns);
376        UpdateBuilder {
377            sql: self.sql.append(returning_sql),
378            schema: PhantomData,
379            state: PhantomData,
380            table: PhantomData,
381            marker: PhantomData,
382            row: PhantomData,
383            grouped: PhantomData,
384        }
385    }
386}