Skip to main content

nautilus_core/
delete.rs

1//! DELETE query AST and builder.
2
3use crate::column::ColumnMarker;
4use crate::error::{Error, Result};
5use crate::expr::Expr;
6
7/// DELETE query AST node.
8#[derive(Debug, Clone, PartialEq)]
9pub struct Delete {
10    /// Table name.
11    pub table: String,
12    /// WHERE clause.
13    pub filter: Option<Expr>,
14    /// Columns to return (RETURNING clause). Empty = no RETURNING.
15    pub returning: Vec<ColumnMarker>,
16}
17
18impl Delete {
19    /// Creates a new DELETE query builder for the given table.
20    pub fn from_table(table: impl Into<String>) -> DeleteBuilder {
21        DeleteBuilder {
22            table: table.into(),
23            filter: None,
24            returning: Vec::new(),
25        }
26    }
27}
28
29/// Reserved capacities for the `Vec`s maintained by a [`DeleteBuilder`].
30#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
31pub struct DeleteCapacity {
32    /// Expected number of `RETURNING` columns.
33    pub returning: usize,
34}
35
36/// Builder for DELETE queries.
37#[derive(Debug, Clone)]
38pub struct DeleteBuilder {
39    table: String,
40    filter: Option<Expr>,
41    returning: Vec<ColumnMarker>,
42}
43
44impl DeleteBuilder {
45    /// Reserve capacity for the builder's internal vectors.
46    #[must_use]
47    pub fn with_capacity(mut self, capacity: DeleteCapacity) -> Self {
48        self.returning.reserve(capacity.returning);
49        self
50    }
51
52    /// Adds a WHERE clause filter.
53    #[must_use]
54    pub fn filter(mut self, expr: Expr) -> Self {
55        self.filter = Some(expr);
56        self
57    }
58
59    /// Sets the RETURNING clause columns.
60    #[must_use]
61    pub fn returning(mut self, columns: Vec<ColumnMarker>) -> Self {
62        self.returning = columns;
63        self
64    }
65
66    /// Builds the final DELETE query.
67    ///
68    /// # Errors
69    ///
70    /// Returns an error if the table name is empty.
71    pub fn build(self) -> Result<Delete> {
72        if self.table.is_empty() {
73            return Err(Error::MissingField("table".to_string()));
74        }
75
76        Ok(Delete {
77            table: self.table,
78            filter: self.filter,
79            returning: self.returning,
80        })
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87    use crate::value::Value;
88
89    #[test]
90    fn test_simple_delete() {
91        let delete = Delete::from_table("users").build().unwrap();
92
93        assert_eq!(delete.table, "users");
94        assert!(delete.filter.is_none());
95        assert!(delete.returning.is_empty());
96    }
97
98    #[test]
99    fn test_delete_with_filter() {
100        let delete = Delete::from_table("users")
101            .filter(Expr::column("id").eq(Expr::param(Value::I64(1))))
102            .build()
103            .unwrap();
104
105        assert!(delete.filter.is_some());
106    }
107
108    #[test]
109    fn test_delete_with_returning() {
110        let delete = Delete::from_table("users")
111            .returning(vec![
112                ColumnMarker::new("users", "id"),
113                ColumnMarker::new("users", "email"),
114            ])
115            .build()
116            .unwrap();
117
118        assert_eq!(delete.returning.len(), 2);
119        assert_eq!(delete.returning[0].name, "id");
120        assert_eq!(delete.returning[1].name, "email");
121    }
122
123    #[test]
124    fn test_delete_with_complex_filter() {
125        let filter = Expr::column("id")
126            .ge(Expr::param(Value::I64(10)))
127            .and(Expr::column("active").eq(Expr::param(Value::Bool(false))));
128
129        let delete = Delete::from_table("users")
130            .filter(filter)
131            .returning(vec![
132                ColumnMarker::new("users", "id"),
133                ColumnMarker::new("users", "email"),
134            ])
135            .build()
136            .unwrap();
137
138        assert!(delete.filter.is_some());
139        assert_eq!(delete.returning.len(), 2);
140    }
141
142    #[test]
143    fn test_missing_table() {
144        let result = Delete::from_table("").build();
145
146        assert!(result.is_err());
147    }
148}