Skip to main content

drizzle_sqlite/builder/
delete.rs

1//! The DELETE builder: [`DeleteBuilder`] and its states.
2//!
3//! Start a DELETE with [`QueryBuilder::delete`](super::QueryBuilder::delete).
4
5use crate::values::SQLiteValue;
6use core::marker::PhantomData;
7use drizzle_core::ToSQL;
8
9//------------------------------------------------------------------------------
10// Type State Markers
11//------------------------------------------------------------------------------
12
13pub use drizzle_core::builder::{DeleteInitial, DeleteReturningSet, DeleteWhereSet};
14
15//------------------------------------------------------------------------------
16// DeleteBuilder Definition
17//------------------------------------------------------------------------------
18
19/// A DELETE query being built for `SQLite`.
20///
21/// This is [`QueryBuilder`](super::QueryBuilder) in one of the `Delete*`
22/// states. Start it with [`QueryBuilder::delete`](super::QueryBuilder::delete).
23///
24/// # Clause order
25///
26/// 1. `where` (required): the rows to delete. `r#where(true)` deletes every
27///    row.
28/// 2. Optionally [`returning`](Self::returning).
29///
30/// The WHERE condition and the RETURNING columns may only reference the
31/// table being deleted from; other tables do not compile.
32///
33/// # Examples
34///
35/// ```rust
36/// # mod drizzle {
37/// #     pub mod core { pub use drizzle_core::*; }
38/// #     pub mod error { pub use drizzle_core::error::*; }
39/// #     pub mod types { pub use drizzle_types::*; }
40/// #     pub mod migrations { pub use drizzle_migrations::*; }
41/// #     pub use drizzle_types::Dialect;
42/// #     pub use drizzle_types as ddl;
43/// #     pub mod sqlite {
44/// #             pub use drizzle_sqlite::{*, attrs::*};
45/// #             #[cfg(feature = "rusqlite")]
46/// #             pub mod rusqlite { pub use ::rusqlite::{Error, Result, Row, types}; }
47/// #             #[cfg(feature = "libsql")]
48/// #             pub mod libsql { pub use ::libsql::{Row, Value}; }
49/// #             #[cfg(feature = "turso")]
50/// #             pub mod turso { pub use ::turso::{Error, IntoValue, Result, Row, Value}; }
51/// #         pub mod prelude {
52/// #             pub use drizzle_macros::{SQLiteTable, SQLiteSchema};
53/// #             pub use drizzle_sqlite::{*, attrs::*};
54/// #             pub use drizzle_core::*;
55/// #         }
56/// #     }
57/// # }
58/// use drizzle::sqlite::prelude::*;
59/// use drizzle::core::expr::{eq, lt};
60/// use drizzle::sqlite::builder::QueryBuilder;
61///
62/// #[SQLiteTable(name = "users")]
63/// struct User {
64///     #[column(primary)]
65///     id: i32,
66///     name: String,
67///     email: Option<String>,
68/// }
69///
70/// #[derive(SQLiteSchema)]
71/// struct Schema {
72///     user: User,
73/// }
74///
75/// let builder = QueryBuilder::new::<Schema>();
76/// let Schema { user } = Schema::new();
77///
78/// // Delete specific row
79/// let query = builder
80///     .delete(user)
81///     .r#where(eq(user.id, 1));
82/// assert_eq!(query.to_sql().sql(), r#"DELETE FROM "users" WHERE "users"."id" = ?"#);
83///
84/// // Delete multiple rows
85/// let query = builder
86///     .delete(user)
87///     .r#where(lt(user.id, 100));
88/// assert_eq!(query.to_sql().sql(), r#"DELETE FROM "users" WHERE "users"."id" < ?"#);
89/// ```
90///
91/// With RETURNING:
92///
93/// ```rust
94/// # mod drizzle {
95/// #     pub mod core { pub use drizzle_core::*; }
96/// #     pub mod error { pub use drizzle_core::error::*; }
97/// #     pub mod types { pub use drizzle_types::*; }
98/// #     pub mod migrations { pub use drizzle_migrations::*; }
99/// #     pub use drizzle_types::Dialect;
100/// #     pub use drizzle_types as ddl;
101/// #     pub mod sqlite {
102/// #             pub use drizzle_sqlite::{*, attrs::*};
103/// #             #[cfg(feature = "rusqlite")]
104/// #             pub mod rusqlite { pub use ::rusqlite::{Error, Result, Row, types}; }
105/// #             #[cfg(feature = "libsql")]
106/// #             pub mod libsql { pub use ::libsql::{Row, Value}; }
107/// #             #[cfg(feature = "turso")]
108/// #             pub mod turso { pub use ::turso::{Error, IntoValue, Result, Row, Value}; }
109/// #         pub mod prelude {
110/// #             pub use drizzle_macros::{SQLiteTable, SQLiteSchema};
111/// #             pub use drizzle_sqlite::{*, attrs::*};
112/// #             pub use drizzle_core::*;
113/// #         }
114/// #     }
115/// # }
116/// # use drizzle::sqlite::prelude::*;
117/// # use drizzle::core::expr::eq;
118/// # use drizzle::sqlite::builder::QueryBuilder;
119/// # #[SQLiteTable(name = "users")] struct User { #[column(primary)] id: i32, name: String }
120/// # #[derive(SQLiteSchema)] struct Schema { user: User }
121/// # let builder = QueryBuilder::new::<Schema>();
122/// # let Schema { user } = Schema::new();
123/// let query = builder
124///     .delete(user)
125///     .r#where(eq(user.id, 1))
126///     .returning((user.id, user.name));
127/// assert_eq!(
128///     query.to_sql().sql(),
129///     r#"DELETE FROM "users" WHERE "users"."id" = ? RETURNING "users"."id", "users"."name""#
130/// );
131/// ```
132///
133/// `r#where(true)` deletes every row:
134///
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::{*, attrs::*};
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::sqlite::builder::QueryBuilder;
160/// # #[SQLiteTable(name = "logs")] struct Log { #[column(primary)] id: i32, message: String }
161/// # #[derive(SQLiteSchema)] struct Schema { log: Log }
162/// # let builder = QueryBuilder::new::<Schema>();
163/// # let Schema { log } = Schema::new();
164/// let query = builder.delete(log).r#where(true);
165/// assert_eq!(query.to_sql().sql(), r#"DELETE FROM "logs" WHERE ?"#);
166/// ```
167pub type DeleteBuilder<'a, Schema, State, Table, Marker = (), Row = ()> =
168    super::QueryBuilder<'a, Schema, State, Table, Marker, Row>;
169
170type ReturningMarker<Table, Columns> = drizzle_core::Scoped<
171    <Columns as drizzle_core::IntoSelectTarget>::Marker,
172    drizzle_core::Cons<Table, drizzle_core::Nil>,
173>;
174
175type ReturningRow<Table, Columns> =
176    <<Columns as drizzle_core::IntoSelectTarget>::Marker as drizzle_core::ResolveRow<Table>>::Row;
177
178type ReturningBuilder<'a, S, T, Columns> = DeleteBuilder<
179    'a,
180    S,
181    DeleteReturningSet,
182    T,
183    ReturningMarker<T, Columns>,
184    ReturningRow<T, Columns>,
185>;
186
187//------------------------------------------------------------------------------
188// Initial State Implementation
189//------------------------------------------------------------------------------
190
191impl<'a, S, T> DeleteBuilder<'a, S, DeleteInitial, T> {
192    /// Adds a WHERE clause that picks the rows to delete.
193    ///
194    /// A DELETE runs only with a WHERE clause, so a forgotten condition does
195    /// not empty the table; `r#where(true)` deletes every row. The condition
196    /// must be a boolean expression over the target table's columns.
197    ///
198    /// # Examples
199    ///
200    /// ```rust
201    /// # mod drizzle {
202    /// #     pub mod core { pub use drizzle_core::*; }
203    /// #     pub mod error { pub use drizzle_core::error::*; }
204    /// #     pub mod types { pub use drizzle_types::*; }
205    /// #     pub mod migrations { pub use drizzle_migrations::*; }
206    /// #     pub use drizzle_types::Dialect;
207    /// #     pub use drizzle_types as ddl;
208    /// #     pub mod sqlite {
209    /// #         pub use drizzle_sqlite::*;
210    /// #         #[cfg(feature = "rusqlite")]
211    /// #         pub mod rusqlite { pub use ::rusqlite::{Error, Result, Row, types}; }
212    /// #         #[cfg(feature = "libsql")]
213    /// #         pub mod libsql { pub use ::libsql::{Row, Value}; }
214    /// #         #[cfg(feature = "turso")]
215    /// #         pub mod turso { pub use ::turso::{Error, IntoValue, Result, Row, Value}; }
216    /// #         pub mod prelude {
217    /// #             pub use drizzle_macros::{SQLiteTable, SQLiteSchema};
218    /// #             pub use drizzle_sqlite::{*, attrs::*};
219    /// #             pub use drizzle_core::*;
220    /// #         }
221    /// #     }
222    /// # }
223    /// # use drizzle::sqlite::prelude::*;
224    /// # use drizzle::core::expr::{eq, gt, and, or};
225    /// # use drizzle::sqlite::builder::QueryBuilder;
226    /// # #[SQLiteTable(name = "users")] struct User { #[column(primary)] id: i32, name: String, age: Option<i32> }
227    /// # #[derive(SQLiteSchema)] struct Schema { user: User }
228    /// # let builder = QueryBuilder::new::<Schema>();
229    /// # let Schema { user } = Schema::new();
230    /// // Delete specific row by ID
231    /// let query = builder
232    ///     .delete(user)
233    ///     .r#where(eq(user.id, 1));
234    /// assert_eq!(query.to_sql().sql(), r#"DELETE FROM "users" WHERE "users"."id" = ?"#);
235    ///
236    /// // Delete with complex conditions
237    /// let query = builder
238    ///     .delete(user)
239    ///     .r#where(and(
240    ///         gt(user.id, 100),
241    ///         or(eq(user.name, "test"), eq(user.age, 0))
242    ///     ));
243    /// assert_eq!(
244    ///     query.to_sql().sql(),
245    ///     r#"DELETE FROM "users" WHERE ("users"."id" > ? AND ("users"."name" = ? OR "users"."age" = ?))"#
246    /// );
247    /// ```
248    #[inline]
249    pub fn r#where<E, ScopeProof>(self, condition: E) -> DeleteBuilder<'a, S, DeleteWhereSet, T>
250    where
251        E: drizzle_core::expr::ExprSources,
252        E::Sources:
253            drizzle_core::scope::SourcesIn<drizzle_core::Cons<T, drizzle_core::Nil>, ScopeProof>,
254        E: drizzle_core::expr::Expr<'a, SQLiteValue<'a>>,
255        E::SQLType: drizzle_core::types::BooleanLike,
256    {
257        let where_sql = crate::helpers::r#where(condition);
258        DeleteBuilder {
259            sql: self.sql.append(where_sql),
260            schema: PhantomData,
261            state: PhantomData,
262            table: PhantomData,
263            marker: PhantomData,
264            row: PhantomData,
265            grouped: PhantomData,
266        }
267    }
268}
269
270//------------------------------------------------------------------------------
271// Post-WHERE Implementation
272//------------------------------------------------------------------------------
273
274impl<'a, S, T> DeleteBuilder<'a, S, DeleteWhereSet, T> {
275    /// Adds a RETURNING clause after WHERE. See
276    /// [`returning`](DeleteBuilder::returning).
277    #[inline]
278    pub fn returning<Columns, ScopeProof>(
279        self,
280        columns: Columns,
281    ) -> ReturningBuilder<'a, S, T, Columns>
282    where
283        Columns: drizzle_core::expr::ExprSources,
284        Columns::Sources:
285            drizzle_core::scope::SourcesIn<drizzle_core::Cons<T, drizzle_core::Nil>, ScopeProof>,
286        Columns: ToSQL<'a, SQLiteValue<'a>> + drizzle_core::IntoSelectTarget,
287        Columns::Marker: drizzle_core::ResolveRow<T>,
288    {
289        let returning_sql = crate::helpers::returning(columns);
290        DeleteBuilder {
291            sql: self.sql.append(returning_sql),
292            schema: PhantomData,
293            state: PhantomData,
294            table: PhantomData,
295            marker: PhantomData,
296            row: PhantomData,
297            grouped: PhantomData,
298        }
299    }
300}