Skip to main content

icydb_core/db/query/fluent/
delete.rs

1use crate::{
2    db::{
3        DbSession,
4        executor::ExecutablePlan,
5        query::{
6            explain::ExplainPlan,
7            expr::{FilterExpr, SortExpr},
8            intent::{PlannedQuery, Query, QueryError},
9            predicate::Predicate,
10        },
11        response::Response,
12    },
13    traits::{EntityKind, EntityValue, SingletonEntity},
14    types::Id,
15};
16
17///
18/// FluentDeleteQuery
19///
20/// Session-bound delete query wrapper.
21/// This type owns *intent construction* and *execution routing only*.
22/// All result projection and cardinality handling lives on `Response<E>`.
23///
24
25pub struct FluentDeleteQuery<'a, E>
26where
27    E: EntityKind,
28{
29    session: &'a DbSession<E::Canister>,
30    query: Query<E>,
31}
32
33impl<'a, E> FluentDeleteQuery<'a, E>
34where
35    E: EntityKind,
36{
37    pub(crate) const fn new(session: &'a DbSession<E::Canister>, query: Query<E>) -> Self {
38        Self { session, query }
39    }
40
41    // ------------------------------------------------------------------
42    // Intent inspection
43    // ------------------------------------------------------------------
44
45    #[must_use]
46    pub const fn query(&self) -> &Query<E> {
47        &self.query
48    }
49
50    // ------------------------------------------------------------------
51    // Intent builders (pure)
52    // ------------------------------------------------------------------
53
54    /// Set the access path to a single typed primary-key value.
55    ///
56    /// `Id<E>` is treated as a plain query input value here. It does not grant access.
57    #[must_use]
58    pub fn by_id(mut self, id: Id<E>) -> Self {
59        self.query = self.query.by_id(id.key());
60        self
61    }
62
63    /// Set the access path to multiple typed primary-key values.
64    ///
65    /// IDs are public and may come from untrusted input sources.
66    #[must_use]
67    pub fn by_ids<I>(mut self, ids: I) -> Self
68    where
69        I: IntoIterator<Item = Id<E>>,
70    {
71        self.query = self.query.by_ids(ids.into_iter().map(|id| id.key()));
72        self
73    }
74
75    // ------------------------------------------------------------------
76    // Query Refinement
77    // ------------------------------------------------------------------
78
79    #[must_use]
80    pub fn filter(mut self, predicate: Predicate) -> Self {
81        self.query = self.query.filter(predicate);
82        self
83    }
84
85    pub fn filter_expr(mut self, expr: FilterExpr) -> Result<Self, QueryError> {
86        self.query = self.query.filter_expr(expr)?;
87        Ok(self)
88    }
89
90    pub fn sort_expr(mut self, expr: SortExpr) -> Result<Self, QueryError> {
91        self.query = self.query.sort_expr(expr)?;
92        Ok(self)
93    }
94
95    #[must_use]
96    pub fn order_by(mut self, field: impl AsRef<str>) -> Self {
97        self.query = self.query.order_by(field);
98        self
99    }
100
101    #[must_use]
102    pub fn order_by_desc(mut self, field: impl AsRef<str>) -> Self {
103        self.query = self.query.order_by_desc(field);
104        self
105    }
106
107    #[must_use]
108    pub fn limit(mut self, limit: u32) -> Self {
109        self.query = self.query.limit(limit);
110        self
111    }
112
113    // ------------------------------------------------------------------
114    // Planning / diagnostics
115    // ------------------------------------------------------------------
116
117    pub fn explain(&self) -> Result<ExplainPlan, QueryError> {
118        self.query.explain()
119    }
120
121    pub fn planned(&self) -> Result<PlannedQuery<E>, QueryError> {
122        self.query.planned()
123    }
124
125    /// Compile this fluent delete intent into executor runtime state.
126    pub fn plan(&self) -> Result<ExecutablePlan<E>, QueryError> {
127        self.planned().map(ExecutablePlan::from)
128    }
129
130    // ------------------------------------------------------------------
131    // Execution (minimal core surface)
132    // ------------------------------------------------------------------
133
134    /// Execute this delete using the session's policy settings.
135    ///
136    /// All result inspection and projection is performed on `Response<E>`.
137    pub fn execute(&self) -> Result<Response<E>, QueryError>
138    where
139        E: EntityValue,
140    {
141        self.session.execute_query(self.query())
142    }
143
144    /// Execute and return whether any rows were affected.
145    pub fn is_empty(&self) -> Result<bool, QueryError>
146    where
147        E: EntityValue,
148    {
149        Ok(self.execute()?.is_empty())
150    }
151
152    /// Execute and return the number of affected rows.
153    pub fn count(&self) -> Result<u32, QueryError>
154    where
155        E: EntityValue,
156    {
157        Ok(self.execute()?.count())
158    }
159
160    /// Execute and require exactly one affected row.
161    pub fn require_one(&self) -> Result<(), QueryError>
162    where
163        E: EntityValue,
164    {
165        self.execute()?.require_one()?;
166        Ok(())
167    }
168
169    /// Execute and require at least one affected row.
170    pub fn require_some(&self) -> Result<(), QueryError>
171    where
172        E: EntityValue,
173    {
174        self.execute()?.require_some()?;
175        Ok(())
176    }
177}
178
179impl<E> FluentDeleteQuery<'_, E>
180where
181    E: EntityKind + SingletonEntity,
182    E::Key: Default,
183{
184    /// Delete the singleton entity.
185    #[must_use]
186    pub fn only(mut self) -> Self {
187        self.query = self.query.only();
188        self
189    }
190}