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}