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