drizzle_sqlite/builder/
update.rs

1use crate::common::SQLiteSchemaType;
2use crate::traits::SQLiteTable;
3use crate::values::SQLiteValue;
4use drizzle_core::{SQL, 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/// use drizzle_sqlite::builder::QueryBuilder;
62/// use drizzle_macros::{SQLiteTable, SQLiteSchema};
63/// use drizzle_core::{ToSQL, expressions::conditions::eq};
64///
65/// #[SQLiteTable(name = "users")]
66/// struct User {
67///     #[integer(primary)]
68///     id: i32,
69///     #[text]
70///     name: String,
71///     #[text]
72///     email: Option<String>,
73/// }
74///
75/// #[derive(SQLiteSchema)]
76/// struct Schema {
77///     user: User,
78/// }
79///
80/// let builder = QueryBuilder::new::<Schema>();
81/// let Schema { user } = Schema::new();
82///
83/// // Basic UPDATE
84/// let query = builder
85///     .update(user)
86///     .set(UpdateUser::default().with_name("Alice Updated"))
87///     .r#where(eq(user.id, 1));
88/// assert_eq!(
89///     query.to_sql().sql(),
90///     r#"UPDATE "users" SET "name" = ? WHERE "users"."id" = ?"#
91/// );
92/// ```
93///
94/// ## Advanced Updates
95///
96/// ### Multiple Column Updates
97/// ```rust
98/// # use drizzle_sqlite::builder::QueryBuilder;
99/// # use drizzle_macros::{SQLiteTable, SQLiteSchema};
100/// # use drizzle_core::{ToSQL, expressions::conditions::eq};
101/// # #[SQLiteTable(name = "users")] struct User { #[integer(primary)] id: i32, #[text] name: String, #[text] email: Option<String> }
102/// # #[derive(SQLiteSchema)] struct Schema { user: User }
103/// # let builder = QueryBuilder::new::<Schema>();
104/// # let Schema { user } = Schema::new();
105/// let query = builder
106///     .update(user)
107///     .set(UpdateUser::default()
108///         .with_name("Alice Updated")
109///         .with_email("alice.new@example.com"))
110///     .r#where(eq(user.id, 1));
111/// ```
112///
113/// ### UPDATE with RETURNING
114/// ```rust
115/// # use drizzle_sqlite::builder::QueryBuilder;
116/// # use drizzle_macros::{SQLiteTable, SQLiteSchema};
117/// # use drizzle_core::{ToSQL, expressions::conditions::eq};
118/// # #[SQLiteTable(name = "users")] struct User { #[integer(primary)] id: i32, #[text] name: String, #[integer] age: Option<i32> }
119/// # #[derive(SQLiteSchema)] struct Schema { user: User }
120/// # let builder = QueryBuilder::new::<Schema>();
121/// # let Schema { user } = Schema::new();
122/// let query = builder
123///     .update(user)
124///     .set(UpdateUser::default().with_name("Alice Updated"))
125///     .r#where(eq(user.id, 1))
126///     .returning((user.id, user.name));
127/// ```
128pub type UpdateBuilder<'a, Schema, State, Table> = super::QueryBuilder<'a, Schema, State, Table>;
129
130//------------------------------------------------------------------------------
131// Initial State Implementation
132//------------------------------------------------------------------------------
133
134impl<'a, Schema, Table> UpdateBuilder<'a, Schema, UpdateInitial, Table>
135where
136    Table: SQLiteTable<'a>,
137{
138    /// Specifies which columns to update and their new values.
139    ///
140    /// This method accepts update expressions that specify which columns should
141    /// be modified. You can update single or multiple columns using condition
142    /// functions from `drizzle_core::expressions::conditions`.
143    ///
144    /// # Examples
145    ///
146    /// ```rust
147    /// # use drizzle_sqlite::builder::QueryBuilder;
148    /// # use drizzle_macros::{SQLiteTable, SQLiteSchema};
149    /// # use drizzle_core::{ToSQL, expressions::conditions::{eq, and}};
150    /// # #[SQLiteTable(name = "users")] struct User { #[integer(primary)] id: i32, #[text] name: String, #[text] email: Option<String> }
151    /// # #[derive(SQLiteSchema)] struct Schema { user: User }
152    /// # let builder = QueryBuilder::new::<Schema>();
153    /// # let Schema { user } = Schema::new();
154    /// // Update single column
155    /// let query = builder
156    ///     .update(user)
157    ///     .set(UpdateUser::default().with_name("New Name"));
158    /// assert_eq!(query.to_sql().sql(), r#"UPDATE "users" SET "name" = ?"#);
159    ///
160    /// // Update multiple columns
161    /// let query = builder
162    ///     .update(user)
163    ///     .set(UpdateUser::default().with_name("New Name").with_email("new@example.com"));
164    /// ```
165    #[inline]
166    pub fn set(
167        self,
168        values: Table::Update,
169    ) -> UpdateBuilder<'a, Schema, UpdateSetClauseSet, Table> {
170        let sql = crate::helpers::set::<'a, Table, SQLiteSchemaType, SQLiteValue<'a>>(values);
171        UpdateBuilder {
172            sql: self.sql.append(sql),
173            schema: PhantomData,
174            state: PhantomData,
175            table: PhantomData,
176        }
177    }
178}
179
180//------------------------------------------------------------------------------
181// Post-SET Implementation
182//------------------------------------------------------------------------------
183
184impl<'a, S, T> UpdateBuilder<'a, S, UpdateSetClauseSet, T> {
185    /// Adds a WHERE clause to specify which rows to update.
186    ///
187    /// Without a WHERE clause, all rows in the table would be updated. This method
188    /// allows you to specify conditions to limit which rows are affected by the update.
189    ///
190    /// # Examples
191    ///
192    /// ```rust
193    /// # use drizzle_sqlite::builder::QueryBuilder;
194    /// # use drizzle_macros::{SQLiteTable, SQLiteSchema};
195    /// # use drizzle_core::{ToSQL, expressions::conditions::{eq, gt, and}};
196    /// # #[SQLiteTable(name = "users")] struct User { #[integer(primary)] id: i32, #[text] name: String, #[integer] age: Option<i32> }
197    /// # #[derive(SQLiteSchema)] struct Schema { user: User }
198    /// # let builder = QueryBuilder::new::<Schema>();
199    /// # let Schema { user } = Schema::new();
200    /// // Update specific row by ID
201    /// let query = builder
202    ///     .update(user)
203    ///     .set(UpdateUser::default().with_name("Updated Name"))
204    ///     .r#where(eq(user.id, 1));
205    /// assert_eq!(
206    ///     query.to_sql().sql(),
207    ///     r#"UPDATE "users" SET "name" = ? WHERE "users"."id" = ?"#
208    /// );
209    ///
210    /// // Update multiple rows with complex condition
211    /// let query = builder
212    ///     .update(user)
213    ///     .set(UpdateUser::default().with_name("Updated"))
214    ///     .r#where(and([gt(user.id, 10), eq(user.age, 25)]));
215    /// ```
216    #[inline]
217    pub fn r#where(
218        self,
219        condition: SQL<'a, SQLiteValue<'a>>,
220    ) -> UpdateBuilder<'a, S, UpdateWhereSet, T> {
221        let where_sql = crate::helpers::r#where(condition);
222        UpdateBuilder {
223            sql: self.sql.append(where_sql),
224            schema: PhantomData,
225            state: PhantomData,
226            table: PhantomData,
227        }
228    }
229
230    /// Adds a RETURNING clause and transitions to the ReturningSet state
231    #[inline]
232    pub fn returning(
233        self,
234        columns: impl ToSQL<'a, SQLiteValue<'a>>,
235    ) -> UpdateBuilder<'a, S, UpdateReturningSet, T> {
236        let returning_sql = crate::helpers::returning(columns);
237        UpdateBuilder {
238            sql: self.sql.append(returning_sql),
239            schema: PhantomData,
240            state: PhantomData,
241            table: PhantomData,
242        }
243    }
244}
245
246//------------------------------------------------------------------------------
247// Post-WHERE Implementation
248//------------------------------------------------------------------------------
249
250impl<'a, S, T> UpdateBuilder<'a, S, UpdateWhereSet, T> {
251    /// Adds a RETURNING clause after WHERE
252    #[inline]
253    pub fn returning(
254        self,
255        columns: impl ToSQL<'a, SQLiteValue<'a>>,
256    ) -> UpdateBuilder<'a, S, UpdateReturningSet, T> {
257        let returning_sql = crate::helpers::returning(columns);
258        UpdateBuilder {
259            sql: self.sql.append(returning_sql),
260            schema: PhantomData,
261            state: PhantomData,
262            table: PhantomData,
263        }
264    }
265}