Skip to main content

icydb_core/db/query/fluent/
delete.rs

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