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