icydb_core/db/query/session/
load.rs1use crate::{
2 db::{
3 DbSession,
4 query::{
5 IntentError, Query, QueryError,
6 expr::{FilterExpr, SortExpr},
7 plan::{ExecutablePlan, ExplainPlan},
8 policy::{self, CursorPagingPolicyError},
9 predicate::Predicate,
10 },
11 response::Response,
12 },
13 traits::{CanisterKind, EntityKind, EntityValue, SingletonEntity},
14 types::Id,
15};
16
17pub struct SessionLoadQuery<'a, C, E>
26where
27 C: CanisterKind,
28 E: EntityKind<Canister = C>,
29{
30 session: &'a DbSession<C>,
31 query: Query<E>,
32 cursor_token: Option<String>,
33 cursor_intent_error: Option<IntentError>,
34}
35
36pub struct PagedLoadQuery<'a, C, E>
44where
45 C: CanisterKind,
46 E: EntityKind<Canister = C>,
47{
48 inner: SessionLoadQuery<'a, C, E>,
49}
50
51impl<'a, C, E> SessionLoadQuery<'a, C, E>
52where
53 C: CanisterKind,
54 E: EntityKind<Canister = C>,
55{
56 pub(crate) const fn new(session: &'a DbSession<C>, query: Query<E>) -> Self {
57 Self {
58 session,
59 query,
60 cursor_token: None,
61 cursor_intent_error: None,
62 }
63 }
64
65 #[must_use]
70 pub const fn query(&self) -> &Query<E> {
71 &self.query
72 }
73
74 #[must_use]
82 pub fn by_id(mut self, id: Id<E>) -> Self {
83 self.query = self.query.by_id(id.key());
84 self
85 }
86
87 #[must_use]
91 pub fn by_ids<I>(mut self, ids: I) -> Self
92 where
93 I: IntoIterator<Item = Id<E>>,
94 {
95 self.query = self.query.by_ids(ids.into_iter().map(|id| id.key()));
96 self
97 }
98
99 #[must_use]
104 pub fn filter(mut self, predicate: Predicate) -> Self {
105 self.query = self.query.filter(predicate);
106 self.refresh_cursor_intent_error();
107 self
108 }
109
110 pub fn filter_expr(mut self, expr: FilterExpr) -> Result<Self, QueryError> {
111 self.query = self.query.filter_expr(expr)?;
112 self.refresh_cursor_intent_error();
113 Ok(self)
114 }
115
116 pub fn sort_expr(mut self, expr: SortExpr) -> Result<Self, QueryError> {
117 self.query = self.query.sort_expr(expr)?;
118 self.refresh_cursor_intent_error();
119 Ok(self)
120 }
121
122 #[must_use]
123 pub fn order_by(mut self, field: impl AsRef<str>) -> Self {
124 self.query = self.query.order_by(field);
125 self.refresh_cursor_intent_error();
126 self
127 }
128
129 #[must_use]
130 pub fn order_by_desc(mut self, field: impl AsRef<str>) -> Self {
131 self.query = self.query.order_by_desc(field);
132 self.refresh_cursor_intent_error();
133 self
134 }
135
136 #[must_use]
141 pub fn limit(mut self, limit: u32) -> Self {
142 self.query = self.query.limit(limit);
143 self.refresh_cursor_intent_error();
144 self
145 }
146
147 #[must_use]
152 pub fn offset(mut self, offset: u32) -> Self {
153 self.query = self.query.offset(offset);
154 self.refresh_cursor_intent_error();
155 self
156 }
157
158 #[must_use]
165 pub fn cursor(mut self, token: impl Into<String>) -> Self {
166 self.cursor_token = Some(token.into());
167 self.refresh_cursor_intent_error();
168 self
169 }
170
171 pub fn explain(&self) -> Result<ExplainPlan, QueryError> {
176 self.query.explain()
177 }
178
179 pub fn plan(&self) -> Result<ExecutablePlan<E>, QueryError> {
180 if let Some(err) = self.cursor_intent_error {
181 return Err(QueryError::Intent(err));
182 }
183
184 self.query.plan()
185 }
186
187 pub fn execute(&self) -> Result<Response<E>, QueryError>
193 where
194 E: EntityValue,
195 {
196 self.session.execute_query(self.query())
197 }
198
199 pub fn page(self) -> Result<PagedLoadQuery<'a, C, E>, QueryError> {
211 self.ensure_paged_mode_ready()?;
212
213 Ok(PagedLoadQuery { inner: self })
214 }
215
216 pub fn execute_paged(self) -> Result<(Response<E>, Option<Vec<u8>>), QueryError>
220 where
221 E: EntityValue,
222 {
223 self.page()?.execute()
224 }
225
226 pub fn is_empty(&self) -> Result<bool, QueryError>
232 where
233 E: EntityValue,
234 {
235 Ok(self.execute()?.is_empty())
236 }
237
238 pub fn count(&self) -> Result<u32, QueryError>
240 where
241 E: EntityValue,
242 {
243 Ok(self.execute()?.count())
244 }
245
246 pub fn require_one(&self) -> Result<(), QueryError>
248 where
249 E: EntityValue,
250 {
251 self.execute()?.require_one().map_err(QueryError::Response)
252 }
253
254 pub fn require_some(&self) -> Result<(), QueryError>
256 where
257 E: EntityValue,
258 {
259 self.execute()?.require_some().map_err(QueryError::Response)
260 }
261}
262
263impl<C, E> SessionLoadQuery<'_, C, E>
264where
265 C: CanisterKind,
266 E: EntityKind<Canister = C>,
267{
268 fn paged_intent_error(&self) -> Option<IntentError> {
269 let spec = self.query.load_spec()?;
270
271 policy::validate_cursor_paging_requirements(self.query.has_explicit_order(), spec)
272 .err()
273 .map(|err| match err {
274 CursorPagingPolicyError::CursorRequiresOrder => IntentError::CursorRequiresOrder,
275 CursorPagingPolicyError::CursorRequiresLimit => IntentError::CursorRequiresLimit,
276 CursorPagingPolicyError::CursorWithOffsetUnsupported => {
277 IntentError::CursorWithOffsetUnsupported
278 }
279 })
280 }
281
282 fn refresh_cursor_intent_error(&mut self) {
283 self.cursor_intent_error = self
284 .cursor_token
285 .as_ref()
286 .and_then(|_| self.paged_intent_error());
287 }
288
289 fn ensure_paged_mode_ready(&self) -> Result<(), QueryError> {
290 if let Some(err) = self.paged_intent_error() {
291 return Err(QueryError::Intent(err));
292 }
293
294 if let Some(err) = self.cursor_intent_error {
295 return Err(QueryError::Intent(err));
296 }
297
298 Ok(())
299 }
300}
301
302impl<C, E> SessionLoadQuery<'_, C, E>
303where
304 C: CanisterKind,
305 E: EntityKind<Canister = C> + SingletonEntity,
306 E::Key: Default,
307{
308 #[must_use]
309 pub fn only(mut self) -> Self {
310 self.query = self.query.only();
311 self.refresh_cursor_intent_error();
312 self
313 }
314}
315
316impl<C, E> PagedLoadQuery<'_, C, E>
317where
318 C: CanisterKind,
319 E: EntityKind<Canister = C>,
320{
321 #[must_use]
326 pub const fn query(&self) -> &Query<E> {
327 self.inner.query()
328 }
329
330 #[must_use]
336 pub fn cursor(mut self, token: impl Into<String>) -> Self {
337 self.inner = self.inner.cursor(token);
338 self
339 }
340
341 pub fn execute(self) -> Result<(Response<E>, Option<Vec<u8>>), QueryError>
351 where
352 E: EntityValue,
353 {
354 self.inner.ensure_paged_mode_ready()?;
355
356 self.inner
357 .session
358 .execute_load_query_paged(self.inner.query(), self.inner.cursor_token.as_deref())
359 }
360}