use crate::{
HasDialect, ModelQueryPolicy, Qrafting, Query, QueryOf,
builder::Update,
cte::IntoCtes,
emitter::Emitter,
query::{LowerFilter, Table, TypedCompiled, rewrite_params},
};
type SoftDeleteFn<T> = fn(Query) -> Option<Update<T>>;
pub struct Delete<T> {
pub table: Table<T>,
pub query: Query,
soft_delete: Option<SoftDeleteFn<T>>,
}
pub fn delete_from<T>(table: Table<T>) -> Delete<T> {
Delete {
table,
query: Query::from(table),
soft_delete: None,
}
}
impl<T> Delete<T> {
pub fn with<C>(mut self, ctes: C) -> Self
where
C: IntoCtes,
{
self.query = self.query.with(ctes);
self
}
pub fn with_recursive<C>(mut self, ctes: C) -> Self
where
C: IntoCtes,
{
self.query = self.query.with_recursive(ctes);
self
}
pub fn filter<F>(mut self, clause: F) -> Self
where
F: LowerFilter,
{
self.query = self.query.filter(clause);
self
}
pub fn or_filter<F>(mut self, clause: F) -> Self
where
F: LowerFilter,
{
self.query = self.query.or_filter(clause);
self
}
pub fn into_compiled<D: HasDialect>(mut self) -> TypedCompiled<T> {
if let Some(soft_delete) = self.soft_delete
&& let Some(update) = soft_delete(self.query.clone())
{
return update.into_compiled::<D>();
}
let sql = self.to_sql::<D>();
TypedCompiled {
sql,
params: self.query.params,
data: self.query.data,
marker: std::marker::PhantomData,
}
}
pub fn to_debug_sql<D: HasDialect>(&mut self) -> String {
if let Some(soft_delete) = self.soft_delete
&& let Some(mut update) = soft_delete(self.query.clone())
{
return update.to_debug_sql::<D>();
}
let mut sql = self.to_sql::<D>();
self.query.debug_params(&mut sql).unwrap();
sql
}
pub fn to_sql<D: HasDialect>(&mut self) -> String {
if let Some(soft_delete) = self.soft_delete
&& let Some(mut update) = soft_delete(self.query.clone())
{
return update.to_sql::<D>();
}
let mut writer = String::new();
let mut directives = Vec::new();
let mut indexes = Vec::new();
let mut emitter = Emitter::new(
&mut writer,
&self.query.data,
D::DIALECT,
&mut directives,
&mut indexes,
);
emitter.emit_delete(self).unwrap();
rewrite_params(&indexes, &mut self.query.params);
writer
}
}
impl<M> QueryOf<M>
where
M: Qrafting,
{
pub fn delete_query(self) -> Delete<M> {
let table = Table::new(M::TABLE);
Delete {
table,
query: self.into(),
soft_delete: Some(<M::QueryPolicy as ModelQueryPolicy<M>>::soft_delete),
}
}
pub async fn delete<E>(self, exec: E) -> quex::Result<quex::ExecResult>
where
E: quex::Executor,
{
self.delete_query().execute(exec).await
}
pub fn force_delete_query(self) -> Delete<M> {
let table = Table::new(M::TABLE);
Delete {
table,
query: self.into(),
soft_delete: None,
}
}
pub async fn force_delete<E>(self, exec: E) -> quex::Result<quex::ExecResult>
where
E: quex::Executor,
{
self.force_delete_query().execute(exec).await
}
pub fn restore_query(self) -> Update<M> {
<M::QueryPolicy as ModelQueryPolicy<M>>::restore(self.into())
.expect("QueryOf::restore_query() is only available for soft-deletable qraft models")
}
pub async fn restore<E>(self, exec: E) -> quex::Result<quex::ExecResult>
where
E: quex::Executor,
{
self.restore_query().execute(exec).await
}
}
impl<T> Delete<T> {
pub fn force(mut self) -> Self {
self.soft_delete = None;
self
}
}
impl<T> Delete<T> {
fn from_query(table: Table<T>, query: Query) -> Self {
Self {
table,
query,
soft_delete: None,
}
}
}
impl Query {
pub fn delete_query(self) -> Delete<()> {
let table = self
.base_table_static()
.expect("Query::delete() requires a base table created from a static table source");
Delete::from_query(Table::new(table), self)
}
pub async fn delete<E>(self, exec: E) -> quex::Result<quex::ExecResult>
where
E: quex::Executor,
{
self.delete_query().execute(exec).await
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
Sqlite,
expression::{Col, PredicateExt},
tests::{User, id, table},
};
#[test]
fn test_simple_delete_stmt() {
let stmt = delete_from(table).to_debug_sql::<Sqlite>();
assert_eq!(stmt, r#"delete from "users"; params=[]"#);
}
#[test]
fn test_filter_delete_stmt() {
let stmt = delete_from(table)
.filter("id".bigint().eq(10))
.to_debug_sql::<Sqlite>();
assert_eq!(stmt, r#"delete from "users" where "id" = ?; params=[10]"#);
}
#[test]
fn test_filter_complex_delete_stmt() {
let stmt = delete_from(table)
.filter("id".bigint().eq(10).or("name".bigint().eq(1)))
.filter("username".bigint().eq(10))
.to_debug_sql::<Sqlite>();
assert_eq!(
stmt,
r#"delete from "users" where ("id" = ? or "name" = ?) and "username" = ?; params=[10, 1, 10]"#
);
}
#[test]
fn test_delete_query() {
let stmt = Query::from("users")
.filter(id.eq(1))
.typed::<User>()
.delete_query()
.to_debug_sql::<Sqlite>();
assert_eq!(
stmt,
r#"delete from "users" where "users"."id" = ?; params=[1]"#
);
}
#[test]
fn test_delete_or_filter_combines_with_existing_where_clause() {
let stmt = delete_from(table)
.filter(id.eq(1))
.or_filter(id.eq(2))
.to_debug_sql::<Sqlite>();
assert_eq!(
stmt,
r#"delete from "users" where "users"."id" = ? or "users"."id" = ?; params=[1, 2]"#
);
}
}