icydb_core/db/query/fluent/load/
pagination.rs1use crate::{
7 db::{
8 PagedLoadExecution, PagedLoadExecutionWithTrace, PersistedRow,
9 query::fluent::load::FluentLoadQuery,
10 query::{
11 intent::{IntentError, QueryError},
12 read_intent::{ADMIN_BATCH_ROWS, AdminBatchRequest, PageRequest, ReadIntentKind},
13 },
14 },
15 entity::{EntityKind, EntityValue},
16};
17
18struct PagedLoadQuery<'a, E>
19where
20 E: EntityKind,
21{
22 inner: FluentLoadQuery<'a, E>,
23 read_intent: ReadIntentKind,
24}
25
26#[derive(Clone, Copy)]
27enum PagedTerminal {
28 PublicPage,
29 TrustedAdminBatch,
30}
31
32impl PagedTerminal {
33 const fn read_intent(self) -> ReadIntentKind {
34 match self {
35 Self::PublicPage => ReadIntentKind::PublicPage,
36 Self::TrustedAdminBatch => ReadIntentKind::TrustedAdminBatch,
37 }
38 }
39
40 const fn validate<E>(self, query: &FluentLoadQuery<'_, E>) -> Result<(), QueryError>
41 where
42 E: EntityKind,
43 {
44 match self {
45 Self::PublicPage => Ok(()),
46 Self::TrustedAdminBatch => {
47 if !query.trusted_read_unchecked_enabled() {
48 return Err(QueryError::intent(
49 IntentError::admin_batch_requires_trusted_read(),
50 ));
51 }
52
53 Ok(())
54 }
55 }
56 }
57}
58
59impl<'a, E> FluentLoadQuery<'a, E>
60where
61 E: PersistedRow,
62{
63 pub fn page(self, limit: u32) -> Result<PagedLoadExecution<E>, QueryError>
73 where
74 E: EntityValue,
75 {
76 self.page_request(PageRequest::first(limit))?.execute()
77 }
78
79 pub fn next_page(
84 self,
85 limit: u32,
86 cursor: impl Into<String>,
87 ) -> Result<PagedLoadExecution<E>, QueryError>
88 where
89 E: EntityValue,
90 {
91 self.page_request(PageRequest::next(limit, cursor))?
92 .execute()
93 }
94
95 fn page_request(self, request: PageRequest) -> Result<PagedLoadQuery<'a, E>, QueryError> {
96 let limit = request.effective_limit();
97 let cursor = request.into_cursor();
98
99 self.into_paged_query(limit, cursor, PagedTerminal::PublicPage)
100 }
101
102 pub fn admin_batch(
108 self,
109 request: AdminBatchRequest,
110 ) -> Result<PagedLoadExecution<E>, QueryError>
111 where
112 E: PersistedRow,
113 {
114 let cursor = request.into_cursor();
115 self.into_paged_query(ADMIN_BATCH_ROWS, cursor, PagedTerminal::TrustedAdminBatch)?
116 .execute()
117 }
118
119 fn into_paged_query(
120 self,
121 limit: u32,
122 cursor: Option<String>,
123 terminal: PagedTerminal,
124 ) -> Result<PagedLoadQuery<'a, E>, QueryError> {
125 self.ensure_page_request_owns_cursor()?;
126 terminal.validate(&self)?;
127
128 let mut inner = self.map_query(|query| query.with_load_limit(limit));
129 if let Some(cursor) = cursor {
130 inner = inner.with_cursor_token(cursor);
131 }
132
133 inner.ensure_paged_mode_ready()?;
134
135 Ok(PagedLoadQuery {
136 inner,
137 read_intent: terminal.read_intent(),
138 })
139 }
140}
141
142impl<E> PagedLoadQuery<'_, E>
143where
144 E: PersistedRow,
145{
146 fn execute(self) -> Result<PagedLoadExecution<E>, QueryError>
152 where
153 E: PersistedRow,
154 {
155 self.execute_with_trace()
156 .map(PagedLoadExecutionWithTrace::into_execution)
157 }
158
159 fn execute_with_trace(self) -> Result<PagedLoadExecutionWithTrace<E>, QueryError>
165 where
166 E: PersistedRow,
167 {
168 self.inner.ensure_default_read_admission()?;
169 self.execute_with_trace_unchecked()
170 }
171
172 fn execute_with_trace_unchecked(self) -> Result<PagedLoadExecutionWithTrace<E>, QueryError>
173 where
174 E: PersistedRow,
175 {
176 let read_intent = self.read_intent;
179 self.inner
180 .session
181 .execute_load_query_paged_with_trace(
182 self.inner.query(),
183 self.inner.cursor_token.as_deref(),
184 )
185 .map(|execution| execution.with_read_intent(read_intent))
186 }
187}