icydb_core/db/query/fluent/
delete.rs1use crate::{
7 db::{
8 DbSession, EntityResponse, PersistedRow,
9 query::{
10 explain::ExplainPlan,
11 expr::{FilterExpr, OrderTerm},
12 intent::{Query, QueryError},
13 trace::QueryTracePlan,
14 },
15 response::ResponseError,
16 },
17 entity::{EntityKind, EntityValue, SingletonEntity},
18 types::Id,
19 value::OutputValue,
20};
21
22pub struct FluentDeleteQuery<'a, E>
32where
33 E: EntityKind,
34{
35 session: &'a DbSession<E::Canister>,
36 query: Query<E>,
37}
38
39impl<'a, E> FluentDeleteQuery<'a, E>
40where
41 E: PersistedRow,
42{
43 #[doc(hidden)]
46 pub fn project_entity_output_values(
47 &self,
48 entity: &E,
49 slots: &[usize],
50 ) -> Result<Vec<OutputValue>, crate::error::InternalError>
51 where
52 E: EntityValue,
53 {
54 self.session.project_entity_output_values(entity, slots)
55 }
56
57 pub(in crate::db) const fn new(session: &'a DbSession<E::Canister>, query: Query<E>) -> Self {
58 Self { session, query }
59 }
60
61 #[must_use]
67 pub(in crate::db) const fn query(&self) -> &Query<E> {
68 &self.query
69 }
70
71 fn map_query(mut self, map: impl FnOnce(Query<E>) -> Query<E>) -> Self {
72 self.query = map(self.query);
73 self
74 }
75
76 fn map_session_query_output<T>(
80 &self,
81 map: impl FnOnce(&DbSession<E::Canister>, &Query<E>) -> Result<T, QueryError>,
82 ) -> Result<T, QueryError> {
83 map(self.session, self.query())
84 }
85
86 #[must_use]
94 pub fn by_id(self, id: Id<E>) -> Self {
95 self.map_query(|query| query.by_id(id.key()))
96 }
97
98 #[must_use]
102 pub fn by_ids<I>(self, ids: I) -> Self
103 where
104 I: IntoIterator<Item = Id<E>>,
105 {
106 self.map_query(|query| query.by_ids(ids.into_iter().map(|id| id.key())))
107 }
108
109 #[must_use]
115 pub fn filter(self, expr: impl Into<FilterExpr>) -> Self {
116 self.map_query(|query| query.filter(expr))
117 }
118
119 #[must_use]
121 pub fn order_term(self, term: OrderTerm) -> Self {
122 self.map_query(|query| query.order_term(term))
123 }
124
125 #[must_use]
127 pub fn order_terms<I>(self, terms: I) -> Self
128 where
129 I: IntoIterator<Item = OrderTerm>,
130 {
131 self.map_query(|query| query.order_terms(terms))
132 }
133
134 #[must_use]
136 pub fn max_affected(self, limit: u32) -> Self {
137 self.map_query(|query| query.limit(limit))
138 }
139
140 pub fn explain(&self) -> Result<ExplainPlan, QueryError> {
146 self.map_session_query_output(DbSession::explain_query_with_visible_indexes)
147 }
148
149 pub fn plan_hash_hex(&self) -> Result<String, QueryError> {
151 self.map_session_query_output(DbSession::query_plan_hash_hex_with_visible_indexes)
152 }
153
154 pub fn trace(&self) -> Result<QueryTracePlan, QueryError> {
156 self.map_session_query_output(DbSession::trace_query)
157 }
158
159 pub fn execute(&self) -> Result<u32, QueryError>
165 where
166 E: EntityValue,
167 {
168 self.session.execute_delete_count(self.query())
169 }
170
171 pub fn execute_rows(&self) -> Result<EntityResponse<E>, QueryError>
174 where
175 E: EntityValue,
176 {
177 self.session.execute_delete_rows(self.query())
178 }
179
180 pub fn is_empty(&self) -> Result<bool, QueryError>
182 where
183 E: EntityValue,
184 {
185 Ok(self.execute()? == 0)
186 }
187
188 pub fn count(&self) -> Result<u32, QueryError>
190 where
191 E: EntityValue,
192 {
193 self.execute()
194 }
195
196 pub fn require_one(&self) -> Result<(), QueryError>
198 where
199 E: EntityValue,
200 {
201 match self.execute()? {
202 1 => Ok(()),
203 0 => Err(ResponseError::not_found(E::PATH).into()),
204 count => Err(ResponseError::not_unique(E::PATH, count).into()),
205 }
206 }
207
208 pub fn require_some(&self) -> Result<(), QueryError>
210 where
211 E: EntityValue,
212 {
213 if self.execute()? == 0 {
214 return Err(ResponseError::not_found(E::PATH).into());
215 }
216
217 Ok(())
218 }
219}
220
221impl<E> FluentDeleteQuery<'_, E>
222where
223 E: PersistedRow + SingletonEntity,
224 E::Key: Default,
225{
226 #[must_use]
228 pub fn singleton(self) -> Self {
229 self.map_query(Query::singleton)
230 }
231}