icydb_core/db/executor/
delete.rs

1use crate::{
2    Error, Key,
3    db::{
4        Db,
5        executor::FilterEvaluator,
6        primitives::{FilterDsl, FilterExt, IntoFilterExpr},
7        query::{DeleteQuery, QueryPlan, QueryValidate},
8        response::Response,
9    },
10    deserialize,
11    obs::metrics,
12    traits::{EntityKind, FieldValue},
13    view::View,
14};
15use std::marker::PhantomData;
16
17///
18/// DeleteExecutor
19///
20
21#[derive(Clone, Copy)]
22pub struct DeleteExecutor<E: EntityKind> {
23    db: Db<E::Canister>,
24    debug: bool,
25    _marker: PhantomData<E>,
26}
27
28impl<E: EntityKind> DeleteExecutor<E> {
29    #[must_use]
30    pub const fn new(db: Db<E::Canister>, debug: bool) -> Self {
31        Self {
32            db,
33            debug,
34            _marker: PhantomData,
35        }
36    }
37
38    // debug
39    #[must_use]
40    pub const fn debug(mut self) -> Self {
41        self.debug = true;
42        self
43    }
44
45    ///
46    /// SHORTCUT METHODS
47    ///
48
49    /// Delete a row by primary key and return its key.
50    pub fn one_key(self, value: impl FieldValue) -> Result<Key, Error> {
51        self.one(value)?.try_key()
52    }
53
54    /// Delete a row by primary key and return its entity.
55    pub fn one_entity(self, value: impl FieldValue) -> Result<E, Error> {
56        self.one(value)?.try_entity()
57    }
58
59    /// Delete a row by primary key and return its view.
60    pub fn one_view(self, value: impl FieldValue) -> Result<View<E>, Error> {
61        self.one(value)?.try_view()
62    }
63
64    ///
65    /// HELPER METHODS
66    ///
67
68    /// Delete a single matching row.
69    pub fn one(self, value: impl FieldValue) -> Result<Response<E>, Error> {
70        let query = DeleteQuery::new().one::<E>(value);
71        self.execute(query)
72    }
73
74    /// Delete the unit-key row.
75    pub fn only(self) -> Result<Response<E>, Error> {
76        let query = DeleteQuery::new().one::<E>(());
77        self.execute(query)
78    }
79
80    /// Delete multiple rows by primary keys.
81    pub fn many(
82        self,
83        values: impl IntoIterator<Item = impl FieldValue>,
84    ) -> Result<Response<E>, Error> {
85        let query = DeleteQuery::new().many::<E>(values);
86        self.execute(query)
87    }
88
89    /// Delete all rows.
90    pub fn all(self) -> Result<Response<E>, Error> {
91        let query = DeleteQuery::new();
92        self.execute(query)
93    }
94
95    /// Apply a filter builder and delete matches.
96    pub fn filter<F, I>(self, f: F) -> Result<Response<E>, Error>
97    where
98        F: FnOnce(FilterDsl) -> I,
99        I: IntoFilterExpr,
100    {
101        let query = DeleteQuery::new().filter(f);
102        self.execute(query)
103    }
104
105    ///
106    /// EXECUTION METHODS
107    ///
108
109    // explain
110    /// Validate and return the query plan without executing.
111    pub fn explain(self, query: DeleteQuery) -> Result<QueryPlan, Error> {
112        QueryValidate::<E>::validate(&query)?;
113
114        Ok(crate::db::executor::plan_for::<E>(query.filter.as_ref()))
115    }
116
117    // execute
118    /// Execute a delete query and return the removed rows.
119    pub fn execute(self, query: DeleteQuery) -> Result<Response<E>, Error> {
120        let mut span = metrics::Span::<E>::new(metrics::ExecKind::Delete);
121        QueryValidate::<E>::validate(&query)?;
122
123        let ctx = self.db.context::<E>();
124        let plan = crate::db::executor::plan_for::<E>(query.filter.as_ref());
125        let keys = ctx.candidates_from_plan(plan)?; // no deserialization here
126
127        // query prep
128        let limit = query
129            .limit
130            .as_ref()
131            .and_then(|l| l.limit)
132            .map(|l| l as usize);
133        let filter_simplified = query.filter.as_ref().map(|f| f.clone().simplify());
134
135        let mut res: Vec<(Key, E)> = Vec::with_capacity(limit.unwrap_or(0));
136        ctx.with_store_mut(|s| {
137            for dk in keys {
138                // early limit
139                if let Some(max) = limit
140                    && res.len() >= max
141                {
142                    break;
143                }
144
145                // read value
146                let Some(bytes) = s.get(&dk) else {
147                    continue;
148                };
149
150                // deserialize once
151                let Ok(entity) = deserialize::<E>(&bytes) else {
152                    continue;
153                };
154
155                // filter check
156                if let Some(ref f) = filter_simplified
157                    && !FilterEvaluator::new(&entity).eval(f)
158                {
159                    continue;
160                }
161
162                // delete row and remove indexes
163                s.remove(&dk);
164                if !E::INDEXES.is_empty() {
165                    self.remove_indexes(&entity)?;
166                }
167
168                // store result (key + deleted entity)
169                res.push((dk.key(), entity));
170            }
171
172            Ok::<_, Error>(())
173        })??;
174
175        //   canic::cdk::println!("query.delete: deleted keys {deleted_rows:?}");
176
177        crate::db::executor::set_rows_from_len(&mut span, res.len());
178
179        Ok(Response(res))
180    }
181
182    // remove_indexes
183    fn remove_indexes(&self, entity: &E) -> Result<(), Error> {
184        for index in E::INDEXES {
185            let store = self.db.with_index(|reg| reg.try_get_store(index.store))?;
186
187            store.with_borrow_mut(|this| {
188                this.remove_index_entry(entity, index);
189            });
190        }
191
192        Ok(())
193    }
194}