Skip to main content

icydb_core/db/query/
session.rs

1use crate::{
2    db::{
3        DbSession,
4        query::{
5            FieldRef, Query, QueryError,
6            plan::{ExecutablePlan, ExplainPlan},
7            predicate::Predicate,
8        },
9        response::Response,
10    },
11    key::Key,
12    traits::{CanisterKind, EntityKind},
13    view::View,
14};
15
16///
17/// SessionLoadQuery
18///
19/// Fluent, session-bound load query wrapper that keeps intent pure
20/// while routing execution through the `DbSession` boundary.
21///
22
23pub struct SessionLoadQuery<'a, C: CanisterKind, E: EntityKind<Canister = C>> {
24    session: &'a DbSession<C>,
25    query: Query<E>,
26}
27
28impl<'a, C: CanisterKind, E: EntityKind<Canister = C>> SessionLoadQuery<'a, C, E> {
29    pub(crate) const fn new(session: &'a DbSession<C>, query: Query<E>) -> Self {
30        Self { session, query }
31    }
32
33    // ------------------------------------------------------------------
34    // Intent inspection
35    // ------------------------------------------------------------------
36
37    /// Return a reference to the underlying query intent.
38    #[must_use]
39    pub const fn query(&self) -> &Query<E> {
40        &self.query
41    }
42
43    // ------------------------------------------------------------------
44    // Intent builders (pure, no execution)
45    // ------------------------------------------------------------------
46
47    /// Filter by primary key.
48    #[must_use]
49    pub fn key(mut self, key: impl Into<Key>) -> Self {
50        let key = key.into();
51        let field = FieldRef::new(E::PRIMARY_KEY);
52        self.query = self.query.filter(field.eq(key));
53        self
54    }
55
56    /// Add a predicate, implicitly AND-ing with any existing predicate.
57    #[must_use]
58    pub fn filter(mut self, predicate: Predicate) -> Self {
59        self.query = self.query.filter(predicate);
60        self
61    }
62
63    /// Append an ascending sort key.
64    #[must_use]
65    pub fn order_by(mut self, field: impl AsRef<str>) -> Self {
66        self.query = self.query.order_by(field);
67        self
68    }
69
70    /// Append a descending sort key.
71    #[must_use]
72    pub fn order_by_desc(mut self, field: impl AsRef<str>) -> Self {
73        self.query = self.query.order_by_desc(field);
74        self
75    }
76
77    /// Apply a load limit to bound result size.
78    #[must_use]
79    pub fn limit(mut self, limit: u32) -> Self {
80        self.query = self.query.limit(limit);
81        self
82    }
83
84    /// Apply a load offset.
85    #[must_use]
86    pub fn offset(mut self, offset: u64) -> Self {
87        self.query = self.query.offset(offset);
88        self
89    }
90
91    // ------------------------------------------------------------------
92    // Planning / diagnostics
93    // ------------------------------------------------------------------
94
95    /// Explain this query without executing it.
96    pub fn explain(&self) -> Result<ExplainPlan, QueryError> {
97        self.query.explain()
98    }
99
100    /// Plan this query into an executor-ready plan.
101    pub fn plan(&self) -> Result<ExecutablePlan<E>, QueryError> {
102        self.query.plan()
103    }
104
105    // ------------------------------------------------------------------
106    // Execution (single boundary)
107    // ------------------------------------------------------------------
108
109    /// Execute this query using the session's policy settings.
110    pub fn execute(&self) -> Result<Response<E>, QueryError> {
111        self.session.execute_query(self.query())
112    }
113
114    // ------------------------------------------------------------------
115    // Execution terminals (interpretation)
116    // ------------------------------------------------------------------
117
118    /// Return whether any rows match this query.
119    pub fn exists(&self) -> Result<bool, QueryError> {
120        Ok(self.count()? > 0)
121    }
122
123    /// Execute and return the number of matching rows.
124    pub fn count(&self) -> Result<u64, QueryError> {
125        Ok(self.execute()?.count())
126    }
127
128    /// Execute and return all entities.
129    pub fn all(&self) -> Result<Vec<E>, QueryError> {
130        Ok(self.execute()?.entities())
131    }
132
133    /// Execute and return all results as views.
134    pub fn views(&self) -> Result<Vec<View<E>>, QueryError> {
135        Ok(self.execute()?.views())
136    }
137
138    /// Execute and require exactly one entity.
139    pub fn one(&self) -> Result<E, QueryError> {
140        self.execute()?.entity().map_err(QueryError::Execute)
141    }
142
143    /// Execute and require exactly one view.
144    pub fn view(&self) -> Result<View<E>, QueryError> {
145        self.execute()?.view().map_err(QueryError::Execute)
146    }
147
148    /// Execute and return zero or one entity.
149    pub fn one_opt(&self) -> Result<Option<E>, QueryError> {
150        self.execute()?.try_entity().map_err(QueryError::Execute)
151    }
152
153    /// Execute and return zero or one view.
154    pub fn view_opt(&self) -> Result<Option<View<E>>, QueryError> {
155        self.execute()?.view_opt().map_err(QueryError::Execute)
156    }
157}
158
159///
160/// SessionDeleteQuery
161///
162/// Fluent, session-bound delete query wrapper that keeps query intent pure
163/// while routing execution through the `DbSession` boundary.
164///
165
166pub struct SessionDeleteQuery<'a, C: CanisterKind, E: EntityKind<Canister = C>> {
167    session: &'a DbSession<C>,
168    query: Query<E>,
169}
170
171impl<'a, C: CanisterKind, E: EntityKind<Canister = C>> SessionDeleteQuery<'a, C, E> {
172    pub(crate) const fn new(session: &'a DbSession<C>, query: Query<E>) -> Self {
173        Self { session, query }
174    }
175
176    // ------------------------------------------------------------------
177    // Intent inspection
178    // ------------------------------------------------------------------
179
180    #[must_use]
181    pub const fn query(&self) -> &Query<E> {
182        &self.query
183    }
184
185    // ------------------------------------------------------------------
186    // Intent builders
187    // ------------------------------------------------------------------
188
189    /// Delete by primary key.
190    #[must_use]
191    pub fn key(mut self, key: impl Into<Key>) -> Self {
192        let key = key.into();
193        let field = FieldRef::new(E::PRIMARY_KEY);
194        self.query = self.query.filter(field.eq(key));
195        self
196    }
197
198    /// Add a predicate, implicitly AND-ing with any existing predicate.
199    #[must_use]
200    pub fn filter(mut self, predicate: Predicate) -> Self {
201        self.query = self.query.filter(predicate);
202        self
203    }
204
205    /// Append an ascending sort key.
206    #[must_use]
207    pub fn order_by(mut self, field: impl AsRef<str>) -> Self {
208        self.query = self.query.order_by(field);
209        self
210    }
211
212    /// Append a descending sort key.
213    #[must_use]
214    pub fn order_by_desc(mut self, field: impl AsRef<str>) -> Self {
215        self.query = self.query.order_by_desc(field);
216        self
217    }
218
219    /// Apply a delete limit to bound mutation size.
220    #[must_use]
221    pub fn limit(mut self, limit: u32) -> Self {
222        self.query = self.query.limit(limit);
223        self
224    }
225
226    // ------------------------------------------------------------------
227    // Planning / diagnostics
228    // ------------------------------------------------------------------
229
230    pub fn explain(&self) -> Result<ExplainPlan, QueryError> {
231        self.query.explain()
232    }
233
234    pub fn plan(&self) -> Result<ExecutablePlan<E>, QueryError> {
235        self.query.plan()
236    }
237
238    // ------------------------------------------------------------------
239    // Execution
240    // ------------------------------------------------------------------
241
242    pub fn execute(&self) -> Result<Response<E>, QueryError> {
243        self.session.execute_query(self.query())
244    }
245
246    /// Execute a delete query and return the deleted rows.
247    pub fn delete_rows(&self) -> Result<Response<E>, QueryError> {
248        self.execute()
249    }
250}