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            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: EntityKind,
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    #[must_use]
52    pub const fn query(&self) -> &Query<E> {
53        &self.query
54    }
55
56    // ------------------------------------------------------------------
57    // Intent builders (pure)
58    // ------------------------------------------------------------------
59
60    /// Set the access path to a single typed primary-key value.
61    ///
62    /// `Id<E>` is treated as a plain query input value here. It does not grant access.
63    #[must_use]
64    pub fn by_id(mut self, id: Id<E>) -> Self {
65        self.query = self.query.by_id(id.key());
66        self
67    }
68
69    /// Set the access path to multiple typed primary-key values.
70    ///
71    /// IDs are public and may come from untrusted input sources.
72    #[must_use]
73    pub fn by_ids<I>(mut self, ids: I) -> Self
74    where
75        I: IntoIterator<Item = Id<E>>,
76    {
77        self.query = self.query.by_ids(ids.into_iter().map(|id| id.key()));
78        self
79    }
80
81    // ------------------------------------------------------------------
82    // Query Refinement
83    // ------------------------------------------------------------------
84
85    #[must_use]
86    pub fn filter(mut self, predicate: Predicate) -> Self {
87        self.query = self.query.filter(predicate);
88        self
89    }
90
91    pub fn filter_expr(mut self, expr: FilterExpr) -> Result<Self, QueryError> {
92        self.query = self.query.filter_expr(expr)?;
93        Ok(self)
94    }
95
96    pub fn sort_expr(mut self, expr: SortExpr) -> Result<Self, QueryError> {
97        self.query = self.query.sort_expr(expr)?;
98        Ok(self)
99    }
100
101    #[must_use]
102    pub fn order_by(mut self, field: impl AsRef<str>) -> Self {
103        self.query = self.query.order_by(field);
104        self
105    }
106
107    #[must_use]
108    pub fn order_by_desc(mut self, field: impl AsRef<str>) -> Self {
109        self.query = self.query.order_by_desc(field);
110        self
111    }
112
113    #[must_use]
114    pub fn limit(mut self, limit: u32) -> Self {
115        self.query = self.query.limit(limit);
116        self
117    }
118
119    // ------------------------------------------------------------------
120    // Planning / diagnostics
121    // ------------------------------------------------------------------
122
123    pub fn explain(&self) -> Result<ExplainPlan, QueryError> {
124        self.query.explain()
125    }
126
127    /// Return the stable plan hash for this query.
128    pub fn plan_hash_hex(&self) -> Result<String, QueryError> {
129        self.query.plan_hash_hex()
130    }
131
132    /// Build one trace payload without executing the query.
133    pub fn trace(&self) -> Result<QueryTracePlan, QueryError> {
134        self.session.trace_query(self.query())
135    }
136
137    pub fn planned(&self) -> Result<PlannedQuery<E>, QueryError> {
138        self.query.planned()
139    }
140
141    pub fn plan(&self) -> Result<CompiledQuery<E>, QueryError> {
142        self.query.plan()
143    }
144
145    // ------------------------------------------------------------------
146    // Execution (minimal core surface)
147    // ------------------------------------------------------------------
148
149    /// Execute this delete using the session's policy settings.
150    ///
151    /// All result inspection and projection is performed on `EntityResponse<E>`.
152    pub fn execute(&self) -> Result<EntityResponse<E>, QueryError>
153    where
154        E: EntityValue,
155    {
156        self.session.execute_query(self.query())
157    }
158
159    /// Execute and return whether any rows were affected.
160    pub fn is_empty(&self) -> Result<bool, QueryError>
161    where
162        E: EntityValue,
163    {
164        Ok(self.execute()?.is_empty())
165    }
166
167    /// Execute and return the number of affected rows.
168    pub fn count(&self) -> Result<u32, QueryError>
169    where
170        E: EntityValue,
171    {
172        Ok(self.execute()?.count())
173    }
174
175    /// Execute and require exactly one affected row.
176    pub fn require_one(&self) -> Result<(), QueryError>
177    where
178        E: EntityValue,
179    {
180        self.execute()?.require_one()?;
181        Ok(())
182    }
183
184    /// Execute and require at least one affected row.
185    pub fn require_some(&self) -> Result<(), QueryError>
186    where
187        E: EntityValue,
188    {
189        self.execute()?.require_some()?;
190        Ok(())
191    }
192}
193
194impl<E> FluentDeleteQuery<'_, E>
195where
196    E: EntityKind + SingletonEntity,
197    E::Key: Default,
198{
199    /// Delete the singleton entity.
200    #[must_use]
201    pub fn only(mut self) -> Self {
202        self.query = self.query.only();
203        self
204    }
205}