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