use crate::{
builder::SqlFragment,
expr::Expr,
identifier::{escape_ident, from_qi, QualifiedIdentifier},
};
#[derive(Clone, Debug, Default)]
pub struct DeleteBuilder {
table: Option<SqlFragment>,
using: Vec<SqlFragment>,
where_clauses: Vec<SqlFragment>,
returning: Vec<SqlFragment>,
}
impl DeleteBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn from_table(mut self, qi: &QualifiedIdentifier) -> Self {
self.table = Some(SqlFragment::raw(from_qi(qi)));
self
}
pub fn from_table_as(mut self, qi: &QualifiedIdentifier, alias: &str) -> Self {
self.table = Some(SqlFragment::raw(format!(
"{} AS {}",
from_qi(qi),
escape_ident(alias)
)));
self
}
pub fn using(mut self, table: &str) -> Self {
self.using.push(SqlFragment::raw(escape_ident(table)));
self
}
pub fn where_expr(mut self, expr: Expr) -> Self {
self.where_clauses.push(expr.into_fragment());
self
}
pub fn where_raw(mut self, sql: SqlFragment) -> Self {
self.where_clauses.push(sql);
self
}
pub fn returning(mut self, column: &str) -> Self {
self.returning
.push(SqlFragment::raw(escape_ident(column)));
self
}
pub fn returning_all(mut self) -> Self {
self.returning.push(SqlFragment::raw("*"));
self
}
pub fn build(self) -> SqlFragment {
let mut result = SqlFragment::new();
result.push("DELETE FROM ");
if let Some(table) = self.table {
result.append(table);
}
if !self.using.is_empty() {
result.push(" USING ");
for (i, table) in self.using.into_iter().enumerate() {
if i > 0 {
result.push(", ");
}
result.append(table);
}
}
if !self.where_clauses.is_empty() {
result.push(" WHERE ");
for (i, clause) in self.where_clauses.into_iter().enumerate() {
if i > 0 {
result.push(" AND ");
}
result.append(clause);
}
}
if !self.returning.is_empty() {
result.push(" RETURNING ");
for (i, ret) in self.returning.into_iter().enumerate() {
if i > 0 {
result.push(", ");
}
result.append(ret);
}
}
result
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_simple_delete() {
let qi = QualifiedIdentifier::new("public", "users");
let sql = DeleteBuilder::new()
.from_table(&qi)
.where_expr(Expr::eq("id", 1i64))
.build();
assert!(sql.sql().contains("DELETE FROM"));
assert!(sql.sql().contains("WHERE"));
assert_eq!(sql.params().len(), 1);
}
#[test]
fn test_delete_all() {
let qi = QualifiedIdentifier::unqualified("logs");
let sql = DeleteBuilder::new().from_table(&qi).build();
assert_eq!(sql.sql(), "DELETE FROM \"logs\"");
assert!(sql.params().is_empty());
}
#[test]
fn test_delete_returning() {
let qi = QualifiedIdentifier::unqualified("users");
let sql = DeleteBuilder::new()
.from_table(&qi)
.where_expr(Expr::is_not_null("deleted_at"))
.returning("id")
.returning("email")
.build();
assert!(sql.sql().contains("RETURNING"));
}
#[test]
fn test_delete_with_using() {
let qi = QualifiedIdentifier::unqualified("orders");
let sql = DeleteBuilder::new()
.from_table(&qi)
.using("users")
.where_raw(SqlFragment::raw(
"\"orders\".\"user_id\" = \"users\".\"id\" AND \"users\".\"deleted\" = true",
))
.build();
assert!(sql.sql().contains("USING"));
}
}