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}