mik_sql/builder/
delete.rs

1//! DELETE query builder.
2
3use crate::dialect::{Dialect, Postgres, Sqlite};
4use crate::validate::assert_valid_sql_identifier;
5
6use super::filter::{build_condition_impl, build_filter_expr_impl};
7use super::types::{Filter, FilterExpr, Operator, QueryResult, Value};
8
9/// Builder for DELETE queries.
10#[derive(Debug)]
11#[must_use = "builder does nothing until .build() is called"]
12pub struct DeleteBuilder<D: Dialect> {
13    dialect: D,
14    table: String,
15    filters: Vec<Filter>,
16    filter_expr: Option<FilterExpr>,
17    returning: Vec<String>,
18}
19
20impl<D: Dialect> DeleteBuilder<D> {
21    /// Create a new delete builder.
22    ///
23    /// # Panics
24    ///
25    /// Panics if the table name is not a valid SQL identifier.
26    pub fn new(dialect: D, table: impl Into<String>) -> Self {
27        let table = table.into();
28        assert_valid_sql_identifier(&table, "table");
29        Self {
30            dialect,
31            table,
32            filters: Vec::new(),
33            filter_expr: None,
34            returning: Vec::new(),
35        }
36    }
37
38    /// Add a simple WHERE filter.
39    ///
40    /// # Panics
41    ///
42    /// Panics if the field name is not a valid SQL identifier.
43    pub fn filter(mut self, field: impl Into<String>, op: Operator, value: Value) -> Self {
44        let field = field.into();
45        assert_valid_sql_identifier(&field, "filter field");
46        self.filters.push(Filter { field, op, value });
47        self
48    }
49
50    /// Set a compound filter expression (AND, OR, NOT).
51    /// Use with `simple()`, `and()`, `or()`, `not()` helpers.
52    pub fn filter_expr(mut self, expr: FilterExpr) -> Self {
53        self.filter_expr = Some(expr);
54        self
55    }
56
57    /// Add RETURNING clause (Postgres/SQLite 3.35+).
58    ///
59    /// # Panics
60    ///
61    /// Panics if any column name is not a valid SQL identifier.
62    pub fn returning(mut self, columns: &[&str]) -> Self {
63        for col in columns {
64            assert_valid_sql_identifier(col, "returning column");
65        }
66        self.returning = columns.iter().map(|s| (*s).to_string()).collect();
67        self
68    }
69
70    /// Build the DELETE query.
71    pub fn build(self) -> QueryResult {
72        let mut sql = String::new();
73        let mut params = Vec::new();
74        let mut param_idx = 1usize;
75
76        // DELETE FROM table
77        sql.push_str(&format!("DELETE FROM {}", self.table));
78
79        // WHERE clause - combine filter_expr and simple filters
80        let has_filter_expr = self.filter_expr.is_some();
81        let has_simple_filters = !self.filters.is_empty();
82
83        if has_filter_expr || has_simple_filters {
84            sql.push_str(" WHERE ");
85            let mut all_conditions = Vec::new();
86
87            if let Some(ref expr) = self.filter_expr {
88                let (condition, new_params, new_idx) =
89                    build_filter_expr_impl(&self.dialect, expr, param_idx);
90                all_conditions.push(condition);
91                params.extend(new_params);
92                param_idx = new_idx;
93            }
94
95            for filter in &self.filters {
96                let (condition, new_params, new_idx) =
97                    build_condition_impl(&self.dialect, filter, param_idx);
98                all_conditions.push(condition);
99                params.extend(new_params);
100                param_idx = new_idx;
101            }
102
103            sql.push_str(&all_conditions.join(" AND "));
104        }
105
106        // RETURNING clause
107        if !self.returning.is_empty() {
108            sql.push_str(&format!(" RETURNING {}", self.returning.join(", ")));
109        }
110
111        QueryResult { sql, params }
112    }
113}
114
115/// Create a DELETE builder for Postgres.
116pub fn delete(table: impl Into<String>) -> DeleteBuilder<Postgres> {
117    DeleteBuilder::new(Postgres, table)
118}
119
120/// Create a DELETE builder for `SQLite`.
121pub fn delete_sqlite(table: impl Into<String>) -> DeleteBuilder<Sqlite> {
122    DeleteBuilder::new(Sqlite, table)
123}