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}