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