Skip to main content

icydb_core/db/query/session/
delete.rs

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