Skip to main content

drizzle_sqlite/builder/
delete.rs

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