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    pub fn one_key(self, value: impl FieldValue) -> Result<Key, Error> {
50        self.one(value)?.try_key()
51    }
52
53    pub fn one_entity(self, value: impl FieldValue) -> Result<E, Error> {
54        self.one(value)?.try_entity()
55    }
56
57    pub fn one_view(self, value: impl FieldValue) -> Result<View<E>, Error> {
58        self.one(value)?.try_view()
59    }
60
61    ///
62    /// HELPER METHODS
63    ///
64
65    pub fn one(self, value: impl FieldValue) -> Result<Response<E>, Error> {
66        let query = DeleteQuery::new().one::<E>(value);
67        self.execute(query)
68    }
69
70    pub fn only(self) -> Result<Response<E>, Error> {
71        let query = DeleteQuery::new().one::<E>(());
72        self.execute(query)
73    }
74
75    pub fn many(
76        self,
77        values: impl IntoIterator<Item = impl FieldValue>,
78    ) -> Result<Response<E>, Error> {
79        let query = DeleteQuery::new().many::<E>(values);
80        self.execute(query)
81    }
82
83    pub fn all(self) -> Result<Response<E>, Error> {
84        let query = DeleteQuery::new();
85        self.execute(query)
86    }
87
88    pub fn filter<F, I>(self, f: F) -> Result<Response<E>, Error>
89    where
90        F: FnOnce(FilterDsl) -> I,
91        I: IntoFilterExpr,
92    {
93        let query = DeleteQuery::new().filter(f);
94        self.execute(query)
95    }
96
97    ///
98    /// EXECUTION METHODS
99    ///
100
101    // explain
102    pub fn explain(self, query: DeleteQuery) -> Result<QueryPlan, Error> {
103        QueryValidate::<E>::validate(&query)?;
104
105        Ok(crate::db::executor::plan_for::<E>(query.filter.as_ref()))
106    }
107
108    // execute
109    pub fn execute(self, query: DeleteQuery) -> Result<Response<E>, Error> {
110        let mut span = metrics::Span::<E>::new(metrics::ExecKind::Delete);
111        QueryValidate::<E>::validate(&query)?;
112
113        let ctx = self.db.context::<E>();
114        let plan = crate::db::executor::plan_for::<E>(query.filter.as_ref());
115        let keys = ctx.candidates_from_plan(plan)?; // no deserialization here
116
117        // query prep
118        let limit = query
119            .limit
120            .as_ref()
121            .and_then(|l| l.limit)
122            .map(|l| l as usize);
123        let filter_simplified = query.filter.as_ref().map(|f| f.clone().simplify());
124
125        let mut res: Vec<(Key, E)> = Vec::with_capacity(limit.unwrap_or(0));
126        ctx.with_store_mut(|s| {
127            for dk in keys {
128                // early limit
129                if let Some(max) = limit
130                    && res.len() >= max
131                {
132                    break;
133                }
134
135                // read value
136                let Some(bytes) = s.get(&dk) else {
137                    continue;
138                };
139
140                // deserialize once
141                let Ok(entity) = deserialize::<E>(&bytes) else {
142                    continue;
143                };
144
145                // filter check
146                if let Some(ref f) = filter_simplified
147                    && !FilterEvaluator::new(&entity).eval(f)
148                {
149                    continue;
150                }
151
152                // delete row and remove indexes
153                s.remove(&dk);
154                if !E::INDEXES.is_empty() {
155                    self.remove_indexes(&entity)?;
156                }
157
158                // store result (key + deleted entity)
159                res.push((dk.key(), entity));
160            }
161
162            Ok::<_, Error>(())
163        })??;
164
165        //   canic::cdk::println!("query.delete: deleted keys {deleted_rows:?}");
166
167        crate::db::executor::set_rows_from_len(&mut span, res.len());
168
169        Ok(Response(res))
170    }
171
172    // remove_indexes
173    fn remove_indexes(&self, entity: &E) -> Result<(), Error> {
174        for index in E::INDEXES {
175            let store = self.db.with_index(|reg| reg.try_get_store(index.store))?;
176
177            store.with_borrow_mut(|this| {
178                this.remove_index_entry(entity, index);
179            });
180        }
181
182        Ok(())
183    }
184}