use crate::backend::Backend;
use crate::cond::{render_cond, Binder, Cond};
use crate::error::{QueryError, Result};
use crate::ident;
use crate::log::{LogCategory, Query};
use crate::value::{IntoValue, Value};
#[derive(Debug, Clone)]
struct UsingTable {
table: String,
on: Option<String>,
}
#[derive(Debug, Clone)]
pub struct DeleteQuery {
backend: Backend,
table: String,
using: Vec<UsingTable>,
wheres: Vec<Cond>,
allow_full: bool,
returning: Vec<String>,
}
impl DeleteQuery {
pub fn new(backend: Backend, table: &str) -> Self {
Self {
backend,
table: table.to_string(),
using: Vec::new(),
wheres: Vec::new(),
allow_full: false,
returning: Vec::new(),
}
}
pub fn returning<I: IntoIterator<Item = S>, S: Into<String>>(mut self, cols: I) -> Self {
self.returning = cols.into_iter().map(|s| s.into()).collect();
self
}
pub fn where_eq<C: Into<String>, V: IntoValue>(mut self, col: C, v: V) -> Self {
self.wheres.push(Cond::eq(col, v));
self
}
pub fn where_op<C: Into<String>, V: IntoValue>(mut self, col: C, op: &str, v: V) -> Self {
match Cond::op(col, op, v) {
Ok(c) => self.wheres.push(c),
Err(e) => panic!("medoo_rs: where_op operador inválido: {}", e),
}
self
}
pub fn try_where_op<C: Into<String>, V: IntoValue>(
mut self,
col: C,
op: &str,
v: V,
) -> Result<Self> {
self.wheres.push(Cond::op(col, op, v)?);
Ok(self)
}
pub fn try_where_eq<C: Into<String>, V: IntoValue>(
mut self,
col: C,
v: V,
) -> Result<Self> {
self.wheres.push(Cond::eq(col, v));
Ok(self)
}
pub fn where_cond(mut self, c: Cond) -> Self {
self.wheres.push(c);
self
}
pub fn using<T: Into<String>>(mut self, table: T) -> Self {
self.using.push(UsingTable { table: table.into(), on: None });
self
}
pub fn using_join<T: Into<String>, O: Into<String>>(mut self, table: T, on: O) -> Self {
self.using.push(UsingTable { table: table.into(), on: Some(on.into()) });
self
}
pub fn allow_full_table(mut self) -> Self {
self.allow_full = true;
self
}
pub fn to_sql(&self) -> Result<(String, Vec<Value>)> {
if self.wheres.is_empty() && self.using.is_empty() && !self.allow_full {
return Err(QueryError::MissingWhere("DELETE"));
}
if !self.using.is_empty() && self.backend == Backend::Sqlite {
return Err(QueryError::InvalidOperator(
"SQLite no soporta DELETE ... USING (reescribí como subquery)".to_string(),
));
}
let mut b = Binder::new(self.backend);
let qtable = ident::quote(self.backend, &self.table)?;
let mut sql = match self.backend {
Backend::MySql if !self.using.is_empty() => {
let mut s = format!("DELETE {} FROM {}", qtable, qtable);
for u in &self.using {
let on = u.on.as_deref().ok_or_else(|| QueryError::InvalidOperator(
"MySQL: using requiere ON (usá using_join)".to_string(),
))?;
s.push_str(" INNER JOIN ");
s.push_str(&ident::quote(self.backend, &u.table)?);
s.push_str(" ON ");
s.push_str(&ident::render_eq_join(self.backend, on)?);
}
s
}
_ => {
let mut s = format!("DELETE FROM {}", qtable);
if !self.using.is_empty() {
s.push_str(" USING ");
let parts: Result<Vec<String>> = self.using.iter()
.map(|u| ident::quote(self.backend, &u.table))
.collect();
s.push_str(&parts?.join(", "));
}
s
}
};
let mut where_parts: Vec<String> = Vec::new();
if self.backend != Backend::MySql {
for u in &self.using {
if let Some(on) = &u.on {
where_parts.push(ident::render_eq_join(self.backend, on)?);
}
}
}
for c in &self.wheres {
where_parts.push(render_cond(c, &mut b)?);
}
if !where_parts.is_empty() {
sql.push_str(" WHERE ");
sql.push_str(&where_parts.join(" AND "));
}
if !self.returning.is_empty() {
let parts: Vec<String> = self.returning.iter().map(|c| {
if c == "*" { Ok("*".to_string()) } else { ident::quote(self.backend, c) }
}).collect::<Result<_>>()?;
sql.push_str(" RETURNING ");
sql.push_str(&parts.join(", "));
}
Ok((sql, b.into_params()))
}
}
impl Query for DeleteQuery {
fn category(&self) -> LogCategory {
LogCategory::DELETE
}
fn build_sql(&self) -> Result<(String, Vec<Value>)> {
self.to_sql()
}
}