Skip to main content

reinhardt_query/query/
delete.rs

1//! DELETE statement builder
2//!
3//! This module provides the `DeleteStatement` type for building SQL DELETE queries.
4
5use crate::{
6	expr::{Condition, ConditionHolder, IntoCondition},
7	types::{IntoTableRef, TableRef},
8	value::Values,
9};
10
11use super::{
12	returning::ReturningClause,
13	traits::{QueryBuilderTrait, QueryStatementBuilder, QueryStatementWriter},
14};
15
16/// DELETE statement builder
17///
18/// This struct provides a fluent API for constructing DELETE queries.
19///
20/// # Examples
21///
22/// ```rust,ignore
23/// use reinhardt_query::prelude::*;
24///
25/// let query = Query::delete()
26///     .from_table("users")
27///     .and_where(Expr::col("active").eq(false));
28/// ```
29#[derive(Debug, Clone)]
30pub struct DeleteStatement {
31	pub(crate) table: Option<TableRef>,
32	pub(crate) r#where: ConditionHolder,
33	pub(crate) returning: Option<ReturningClause>,
34}
35
36impl DeleteStatement {
37	/// Create a new DELETE statement
38	pub fn new() -> Self {
39		Self {
40			table: None,
41			r#where: ConditionHolder::new(),
42			returning: None,
43		}
44	}
45
46	/// Take the ownership of data in the current [`DeleteStatement`]
47	pub fn take(&mut self) -> Self {
48		Self {
49			table: self.table.take(),
50			r#where: std::mem::replace(&mut self.r#where, ConditionHolder::new()),
51			returning: self.returning.take(),
52		}
53	}
54
55	/// Set the table to delete from
56	///
57	/// # Examples
58	///
59	/// ```rust,ignore
60	/// use reinhardt_query::prelude::*;
61	///
62	/// let query = Query::delete()
63	///     .from_table("users");
64	/// ```
65	pub fn from_table<T>(&mut self, tbl: T) -> &mut Self
66	where
67		T: IntoTableRef,
68	{
69		self.table = Some(tbl.into_table_ref());
70		self
71	}
72
73	/// Add a condition to the WHERE clause
74	///
75	/// # Examples
76	///
77	/// ```rust,ignore
78	/// use reinhardt_query::prelude::*;
79	///
80	/// let query = Query::delete()
81	///     .from_table("users")
82	///     .and_where(Expr::col("deleted_at").is_not_null())
83	///     .and_where(Expr::col("deleted_at").lt("2020-01-01"));
84	/// ```
85	pub fn and_where<C>(&mut self, condition: C) -> &mut Self
86	where
87		C: IntoCondition,
88	{
89		self.r#where.add_and(condition);
90		self
91	}
92
93	/// Add a conditional WHERE clause.
94	///
95	/// This is an alias for [`and_where`](Self::and_where) that accepts a [`Condition`].
96	pub fn cond_where(&mut self, condition: Condition) -> &mut Self {
97		self.r#where.add_and(condition);
98		self
99	}
100
101	/// Add a RETURNING clause
102	///
103	/// # Examples
104	///
105	/// ```rust,ignore
106	/// use reinhardt_query::prelude::*;
107	///
108	/// let query = Query::delete()
109	///     .from_table("users")
110	///     .and_where(Expr::col("id").eq(1))
111	///     .returning(["id", "name"]);
112	/// ```
113	pub fn returning<I, C>(&mut self, cols: I) -> &mut Self
114	where
115		I: IntoIterator<Item = C>,
116		C: crate::types::IntoColumnRef,
117	{
118		self.returning = Some(ReturningClause::columns(cols));
119		self
120	}
121
122	/// Add a RETURNING * clause
123	///
124	/// # Examples
125	///
126	/// ```rust,ignore
127	/// use reinhardt_query::prelude::*;
128	///
129	/// let query = Query::delete()
130	///     .from_table("users")
131	///     .and_where(Expr::col("id").eq(1))
132	///     .returning_all();
133	/// ```
134	pub fn returning_all(&mut self) -> &mut Self {
135		self.returning = Some(ReturningClause::all());
136		self
137	}
138}
139
140impl Default for DeleteStatement {
141	fn default() -> Self {
142		Self::new()
143	}
144}
145
146impl QueryStatementBuilder for DeleteStatement {
147	fn build_any(&self, query_builder: &dyn QueryBuilderTrait) -> (String, Values) {
148		use crate::backend::{
149			MySqlQueryBuilder, PostgresQueryBuilder, QueryBuilder, SqliteQueryBuilder,
150		};
151		use std::any::Any;
152
153		let any_builder = query_builder as &dyn Any;
154
155		if let Some(pg) = any_builder.downcast_ref::<PostgresQueryBuilder>() {
156			return pg.build_delete(self);
157		}
158
159		if let Some(mysql) = any_builder.downcast_ref::<MySqlQueryBuilder>() {
160			return mysql.build_delete(self);
161		}
162
163		if let Some(sqlite) = any_builder.downcast_ref::<SqliteQueryBuilder>() {
164			return sqlite.build_delete(self);
165		}
166
167		panic!(
168			"Unsupported query builder type. Use PostgresQueryBuilder, MySqlQueryBuilder, or SqliteQueryBuilder."
169		);
170	}
171}
172
173impl QueryStatementWriter for DeleteStatement {}
174
175#[cfg(test)]
176mod tests {
177	use super::*;
178	use crate::expr::{Expr, ExprTrait};
179
180	#[test]
181	fn test_delete_basic() {
182		let mut query = DeleteStatement::new();
183		query.from_table("users");
184
185		assert!(query.table.is_some());
186	}
187
188	#[test]
189	fn test_delete_with_where() {
190		let mut query = DeleteStatement::new();
191		query
192			.from_table("users")
193			.and_where(Expr::col("active").eq(false));
194
195		assert!(query.table.is_some());
196		assert!(!query.r#where.is_empty());
197	}
198
199	#[test]
200	fn test_delete_multiple_conditions() {
201		let mut query = DeleteStatement::new();
202		query
203			.from_table("users")
204			.and_where(Expr::col("active").eq(false))
205			.and_where(Expr::col("deleted_at").is_not_null());
206
207		assert!(!query.r#where.is_empty());
208	}
209
210	#[test]
211	fn test_delete_returning() {
212		let mut query = DeleteStatement::new();
213		query
214			.from_table("users")
215			.and_where(Expr::col("id").eq(1))
216			.returning(["id", "name"]);
217
218		assert!(query.returning.is_some());
219		let returning = query.returning.unwrap();
220		assert!(!returning.is_all());
221	}
222
223	#[test]
224	fn test_delete_returning_all() {
225		let mut query = DeleteStatement::new();
226		query
227			.from_table("users")
228			.and_where(Expr::col("id").eq(1))
229			.returning_all();
230
231		assert!(query.returning.is_some());
232		let returning = query.returning.unwrap();
233		assert!(returning.is_all());
234	}
235
236	#[test]
237	fn test_delete_take() {
238		let mut query = DeleteStatement::new();
239		query.from_table("users");
240
241		let taken = query.take();
242		assert!(taken.table.is_some());
243		assert!(query.table.is_none());
244	}
245}