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}