Skip to main content

drizzle_sqlite/builder/
update.rs

1use crate::common::SQLiteSchemaType;
2use crate::traits::SQLiteTable;
3use crate::values::SQLiteValue;
4use core::fmt::Debug;
5use core::marker::PhantomData;
6use drizzle_core::{SQL, ToSQL};
7
8// Import the ExecutableState trait
9use super::ExecutableState;
10
11#[inline]
12fn append_sql<'a>(
13    mut base: SQL<'a, SQLiteValue<'a>>,
14    fragment: SQL<'a, SQLiteValue<'a>>,
15) -> SQL<'a, SQLiteValue<'a>> {
16    base.append_mut(fragment);
17    base
18}
19
20//------------------------------------------------------------------------------
21// Type State Markers
22//------------------------------------------------------------------------------
23
24/// Marker for the initial state of `UpdateBuilder`
25#[derive(Debug, Clone, Copy, Default)]
26pub struct UpdateInitial;
27
28/// Marker for the state after SET clause
29#[derive(Debug, Clone, Copy, Default)]
30pub struct UpdateSetClauseSet;
31
32/// Marker for the state after WHERE clause
33#[derive(Debug, Clone, Copy, Default)]
34pub struct UpdateWhereSet;
35
36/// Marker for the state after RETURNING clause
37#[derive(Debug, Clone, Copy, Default)]
38pub struct UpdateReturningSet;
39
40// Mark states that can execute update queries
41impl ExecutableState for UpdateSetClauseSet {}
42impl ExecutableState for UpdateWhereSet {}
43impl ExecutableState for UpdateReturningSet {}
44
45//------------------------------------------------------------------------------
46// UpdateBuilder Definition
47//------------------------------------------------------------------------------
48
49/// Builds an UPDATE query specifically for `SQLite`.
50///
51/// `UpdateBuilder` provides a type-safe, fluent API for constructing UPDATE statements
52/// with support for conditional updates, returning clauses, and precise column targeting.
53///
54/// ## Type Parameters
55///
56/// - `Schema`: The database schema type, ensuring only valid tables can be referenced
57/// - `State`: The current builder state, enforcing proper query construction order
58/// - `Table`: The table being updated
59///
60/// ## Query Building Flow
61///
62/// 1. Start with `QueryBuilder::update(table)` to specify the target table
63/// 2. Add `set()` to specify which columns to update and their new values
64/// 3. Optionally add `where()` to limit which rows are updated
65/// 4. Optionally add `returning()` to get updated values back
66///
67/// ## Basic Usage
68///
69/// ```rust
70/// # mod drizzle {
71/// #     pub mod core { pub use drizzle_core::*; }
72/// #     pub mod error { pub use drizzle_core::error::*; }
73/// #     pub mod types { pub use drizzle_types::*; }
74/// #     pub mod migrations { pub use drizzle_migrations::*; }
75/// #     pub use drizzle_types::Dialect;
76/// #     pub use drizzle_types as ddl;
77/// #     pub mod sqlite {
78/// #         pub use drizzle_sqlite::*;
79/// #         pub mod prelude {
80/// #             pub use drizzle_macros::{SQLiteTable, SQLiteSchema};
81/// #             pub use drizzle_sqlite::{*, attrs::*};
82/// #             pub use drizzle_core::*;
83/// #         }
84/// #     }
85/// # }
86/// use drizzle::sqlite::prelude::*;
87/// use drizzle::core::expr::eq;
88/// use drizzle::sqlite::builder::QueryBuilder;
89///
90/// #[SQLiteTable(name = "users")]
91/// struct User {
92///     #[column(primary)]
93///     id: i32,
94///     name: String,
95///     email: Option<String>,
96/// }
97///
98/// #[derive(SQLiteSchema)]
99/// struct Schema {
100///     user: User,
101/// }
102///
103/// let builder = QueryBuilder::new::<Schema>();
104/// let Schema { user } = Schema::new();
105///
106/// // Basic UPDATE
107/// let query = builder
108///     .update(user)
109///     .set(UpdateUser::default().with_name("Alice Updated"))
110///     .r#where(eq(user.id, 1));
111/// assert_eq!(
112///     query.to_sql().sql(),
113///     r#"UPDATE "users" SET "name" = ? WHERE "users"."id" = ?"#
114/// );
115/// ```
116///
117/// ## Advanced Updates
118///
119/// ### Multiple Column Updates
120/// ```rust
121/// # mod drizzle {
122/// #     pub mod core { pub use drizzle_core::*; }
123/// #     pub mod error { pub use drizzle_core::error::*; }
124/// #     pub mod types { pub use drizzle_types::*; }
125/// #     pub mod migrations { pub use drizzle_migrations::*; }
126/// #     pub use drizzle_types::Dialect;
127/// #     pub use drizzle_types as ddl;
128/// #     pub mod sqlite {
129/// #         pub use drizzle_sqlite::*;
130/// #         pub mod prelude {
131/// #             pub use drizzle_macros::{SQLiteTable, SQLiteSchema};
132/// #             pub use drizzle_sqlite::{*, attrs::*};
133/// #             pub use drizzle_core::*;
134/// #         }
135/// #     }
136/// # }
137/// # use drizzle::sqlite::prelude::*;
138/// # use drizzle::core::expr::eq;
139/// # use drizzle::sqlite::builder::QueryBuilder;
140/// # #[SQLiteTable(name = "users")] struct User { #[column(primary)] id: i32, name: String, email: Option<String> }
141/// # #[derive(SQLiteSchema)] struct Schema { user: User }
142/// # let builder = QueryBuilder::new::<Schema>();
143/// # let Schema { user } = Schema::new();
144/// let query = builder
145///     .update(user)
146///     .set(UpdateUser::default()
147///         .with_name("Alice Updated")
148///         .with_email("alice.new@example.com"))
149///     .r#where(eq(user.id, 1));
150/// ```
151///
152/// ### UPDATE with RETURNING
153/// ```rust
154/// # mod drizzle {
155/// #     pub mod core { pub use drizzle_core::*; }
156/// #     pub mod error { pub use drizzle_core::error::*; }
157/// #     pub mod types { pub use drizzle_types::*; }
158/// #     pub mod migrations { pub use drizzle_migrations::*; }
159/// #     pub use drizzle_types::Dialect;
160/// #     pub use drizzle_types as ddl;
161/// #     pub mod sqlite {
162/// #         pub use drizzle_sqlite::*;
163/// #         pub mod prelude {
164/// #             pub use drizzle_macros::{SQLiteTable, SQLiteSchema};
165/// #             pub use drizzle_sqlite::{*, attrs::*};
166/// #             pub use drizzle_core::*;
167/// #         }
168/// #     }
169/// # }
170/// # use drizzle::sqlite::prelude::*;
171/// # use drizzle::core::expr::eq;
172/// # use drizzle::sqlite::builder::QueryBuilder;
173/// # #[SQLiteTable(name = "users")] struct User { #[column(primary)] id: i32, name: String, age: Option<i32> }
174/// # #[derive(SQLiteSchema)] struct Schema { user: User }
175/// # let builder = QueryBuilder::new::<Schema>();
176/// # let Schema { user } = Schema::new();
177/// let query = builder
178///     .update(user)
179///     .set(UpdateUser::default().with_name("Alice Updated"))
180///     .r#where(eq(user.id, 1))
181///     .returning((user.id, user.name));
182/// ```
183pub type UpdateBuilder<'a, Schema, State, Table, Marker = (), Row = ()> =
184    super::QueryBuilder<'a, Schema, State, Table, Marker, Row>;
185
186type ReturningMarker<Table, Columns> = drizzle_core::Scoped<
187    <Columns as drizzle_core::IntoSelectTarget>::Marker,
188    drizzle_core::Cons<Table, drizzle_core::Nil>,
189>;
190
191type ReturningRow<Table, Columns> =
192    <<Columns as drizzle_core::IntoSelectTarget>::Marker as drizzle_core::ResolveRow<Table>>::Row;
193
194type ReturningBuilder<'a, S, T, Columns> = UpdateBuilder<
195    'a,
196    S,
197    UpdateReturningSet,
198    T,
199    ReturningMarker<T, Columns>,
200    ReturningRow<T, Columns>,
201>;
202
203//------------------------------------------------------------------------------
204// Initial State Implementation
205//------------------------------------------------------------------------------
206
207impl<'a, Schema, Table> UpdateBuilder<'a, Schema, UpdateInitial, Table>
208where
209    Table: SQLiteTable<'a>,
210{
211    /// Specifies which columns to update and their new values.
212    ///
213    /// This method accepts update expressions that specify which columns should
214    /// be modified. You can update single or multiple columns using the generated
215    /// update model's `with_*` setters.
216    ///
217    /// # Examples
218    ///
219    /// ```rust
220    /// # mod drizzle {
221    /// #     pub mod core { pub use drizzle_core::*; }
222    /// #     pub mod error { pub use drizzle_core::error::*; }
223    /// #     pub mod types { pub use drizzle_types::*; }
224    /// #     pub mod migrations { pub use drizzle_migrations::*; }
225    /// #     pub use drizzle_types::Dialect;
226    /// #     pub use drizzle_types as ddl;
227    /// #     pub mod sqlite {
228    /// #         pub use drizzle_sqlite::*;
229    /// #         pub mod prelude {
230    /// #             pub use drizzle_macros::{SQLiteTable, SQLiteSchema};
231    /// #             pub use drizzle_sqlite::{*, attrs::*};
232    /// #             pub use drizzle_core::*;
233    /// #         }
234    /// #     }
235    /// # }
236    /// # use drizzle::sqlite::prelude::*;
237    /// # use drizzle::sqlite::builder::QueryBuilder;
238    /// # use drizzle::core::{ToSQL, expr::{eq, and}};
239    /// # #[SQLiteTable(name = "users")] struct User { #[column(primary)] id: i32, name: String, email: Option<String> }
240    /// # #[derive(SQLiteSchema)] struct Schema { user: User }
241    /// # let builder = QueryBuilder::new::<Schema>();
242    /// # let Schema { user } = Schema::new();
243    /// // Update single column
244    /// let query = builder
245    ///     .update(user)
246    ///     .set(UpdateUser::default().with_name("New Name"));
247    /// assert_eq!(query.to_sql().sql(), r#"UPDATE "users" SET "name" = ?"#);
248    ///
249    /// // Update multiple columns
250    /// let query = builder
251    ///     .update(user)
252    ///     .set(UpdateUser::default().with_name("New Name").with_email("new@example.com"));
253    /// ```
254    #[inline]
255    pub fn set(
256        self,
257        values: Table::Update,
258    ) -> UpdateBuilder<'a, Schema, UpdateSetClauseSet, Table> {
259        let sql = crate::helpers::set::<Table, SQLiteSchemaType, SQLiteValue<'a>>(&values);
260        drop(values);
261        UpdateBuilder {
262            sql: append_sql(self.sql, sql),
263            schema: PhantomData,
264            state: PhantomData,
265            table: PhantomData,
266            marker: PhantomData,
267            row: PhantomData,
268            grouped: PhantomData,
269        }
270    }
271}
272
273//------------------------------------------------------------------------------
274// Post-SET Implementation
275//------------------------------------------------------------------------------
276
277impl<'a, S, T> UpdateBuilder<'a, S, UpdateSetClauseSet, T> {
278    /// Adds a WHERE clause to specify which rows to update.
279    ///
280    /// Without a WHERE clause, all rows in the table would be updated. This method
281    /// allows you to specify conditions to limit which rows are affected by the update.
282    ///
283    /// # Examples
284    ///
285    /// ```rust
286    /// # mod drizzle {
287    /// #     pub mod core { pub use drizzle_core::*; }
288    /// #     pub mod error { pub use drizzle_core::error::*; }
289    /// #     pub mod types { pub use drizzle_types::*; }
290    /// #     pub mod migrations { pub use drizzle_migrations::*; }
291    /// #     pub use drizzle_types::Dialect;
292    /// #     pub use drizzle_types as ddl;
293    /// #     pub mod sqlite {
294    /// #         pub use drizzle_sqlite::*;
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: append_sql(self.sql, 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: append_sql(self.sql, 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: append_sql(self.sql, returning_sql),
378            schema: PhantomData,
379            state: PhantomData,
380            table: PhantomData,
381            marker: PhantomData,
382            row: PhantomData,
383            grouped: PhantomData,
384        }
385    }
386}