1pub(crate) mod access;
3pub(crate) mod contracts;
4pub(crate) mod cursor;
5pub(crate) mod diagnostics;
6pub(crate) mod identity;
7pub(crate) mod policy;
8pub(crate) mod query;
9pub(crate) mod registry;
10pub(crate) mod response;
11pub(crate) mod session;
12
13pub(in crate::db) mod codec;
14pub(in crate::db) mod commit;
15pub(in crate::db) mod data;
16pub(in crate::db) mod direction;
17pub(in crate::db) mod executor;
18pub(in crate::db) mod index;
19pub(in crate::db) mod plan;
20pub(in crate::db) mod relation;
21
22pub use codec::cursor::{decode_cursor, encode_cursor};
24pub use contracts::ReadConsistency;
25pub use contracts::ValidateError;
26pub use contracts::{CoercionId, CompareOp, ComparePredicate, Predicate, UnsupportedQueryFeature};
27pub use data::DataStore;
28pub(crate) use data::StorageKey;
29pub use diagnostics::StorageReport;
30pub use executor::{ExecutionAccessPathVariant, ExecutionOptimization, ExecutionTrace};
31pub use identity::{EntityName, IndexName};
32pub use index::IndexStore;
33pub use query::{
34 builder::field::FieldRef,
35 expr::{FilterExpr, SortExpr},
36 fluent::{
37 delete::FluentDeleteQuery,
38 load::{FluentLoadQuery, PagedLoadQuery},
39 },
40 intent::{DeleteSpec, IntentError, LoadSpec, Query, QueryError, QueryMode},
41 plan::{OrderDirection, PlanError},
42};
43pub use registry::StoreRegistry;
44pub use relation::validate_delete_strong_relations_for_source;
45pub use response::{Response, ResponseError, Row, WriteBatchResponse, WriteResponse};
46pub use session::DbSession;
47
48use crate::{
50 db::{
51 commit::{
52 CommitRowOp, PreparedRowCommitOp, ensure_recovered, prepare_row_commit_for_entity,
53 rebuild_secondary_indexes_from_rows, replay_commit_marker_row_ops,
54 },
55 data::RawDataKey,
56 executor::Context,
57 relation::StrongRelationDeleteValidateFn,
58 },
59 error::InternalError,
60 traits::{CanisterKind, EntityIdentity, EntityKind, EntityValue},
61};
62use std::{collections::BTreeSet, marker::PhantomData, thread::LocalKey};
63
64#[derive(Debug)]
71pub struct PagedLoadExecution<E: EntityKind> {
72 response: Response<E>,
73 continuation_cursor: Option<Vec<u8>>,
74}
75
76impl<E: EntityKind> PagedLoadExecution<E> {
77 #[must_use]
79 pub const fn new(response: Response<E>, continuation_cursor: Option<Vec<u8>>) -> Self {
80 Self {
81 response,
82 continuation_cursor,
83 }
84 }
85
86 #[must_use]
88 pub const fn response(&self) -> &Response<E> {
89 &self.response
90 }
91
92 #[must_use]
94 pub fn continuation_cursor(&self) -> Option<&[u8]> {
95 self.continuation_cursor.as_deref()
96 }
97
98 #[must_use]
100 pub fn into_parts(self) -> (Response<E>, Option<Vec<u8>>) {
101 (self.response, self.continuation_cursor)
102 }
103}
104
105impl<E: EntityKind> From<(Response<E>, Option<Vec<u8>>)> for PagedLoadExecution<E> {
106 fn from(value: (Response<E>, Option<Vec<u8>>)) -> Self {
107 let (response, continuation_cursor) = value;
108
109 Self::new(response, continuation_cursor)
110 }
111}
112
113impl<E: EntityKind> From<PagedLoadExecution<E>> for (Response<E>, Option<Vec<u8>>) {
114 fn from(value: PagedLoadExecution<E>) -> Self {
115 value.into_parts()
116 }
117}
118
119#[derive(Debug)]
126pub struct PagedLoadExecutionWithTrace<E: EntityKind> {
127 execution: PagedLoadExecution<E>,
128 execution_trace: Option<ExecutionTrace>,
129}
130
131impl<E: EntityKind> PagedLoadExecutionWithTrace<E> {
132 #[must_use]
134 pub const fn new(
135 response: Response<E>,
136 continuation_cursor: Option<Vec<u8>>,
137 execution_trace: Option<ExecutionTrace>,
138 ) -> Self {
139 Self {
140 execution: PagedLoadExecution::new(response, continuation_cursor),
141 execution_trace,
142 }
143 }
144
145 #[must_use]
147 pub const fn execution(&self) -> &PagedLoadExecution<E> {
148 &self.execution
149 }
150
151 #[must_use]
153 pub const fn response(&self) -> &Response<E> {
154 self.execution.response()
155 }
156
157 #[must_use]
159 pub fn continuation_cursor(&self) -> Option<&[u8]> {
160 self.execution.continuation_cursor()
161 }
162
163 #[must_use]
165 pub const fn execution_trace(&self) -> Option<&ExecutionTrace> {
166 self.execution_trace.as_ref()
167 }
168
169 #[must_use]
171 pub fn into_execution(self) -> PagedLoadExecution<E> {
172 self.execution
173 }
174
175 #[must_use]
177 pub fn into_parts(self) -> (Response<E>, Option<Vec<u8>>, Option<ExecutionTrace>) {
178 let (response, continuation_cursor) = self.execution.into_parts();
179
180 (response, continuation_cursor, self.execution_trace)
181 }
182}
183
184impl<E: EntityKind> From<(Response<E>, Option<Vec<u8>>, Option<ExecutionTrace>)>
185 for PagedLoadExecutionWithTrace<E>
186{
187 fn from(value: (Response<E>, Option<Vec<u8>>, Option<ExecutionTrace>)) -> Self {
188 let (response, continuation_cursor, execution_trace) = value;
189
190 Self::new(response, continuation_cursor, execution_trace)
191 }
192}
193
194impl<E: EntityKind> From<PagedLoadExecutionWithTrace<E>>
195 for (Response<E>, Option<Vec<u8>>, Option<ExecutionTrace>)
196{
197 fn from(value: PagedLoadExecutionWithTrace<E>) -> Self {
198 value.into_parts()
199 }
200}
201
202pub struct Db<C: CanisterKind> {
208 store: &'static LocalKey<StoreRegistry>,
209 entity_runtime_hooks: &'static [EntityRuntimeHooks<C>],
210 _marker: PhantomData<C>,
211}
212
213impl<C: CanisterKind> Db<C> {
214 #[must_use]
215 pub const fn new(store: &'static LocalKey<StoreRegistry>) -> Self {
216 Self::new_with_hooks(store, &[])
217 }
218
219 #[must_use]
220 pub const fn new_with_hooks(
221 store: &'static LocalKey<StoreRegistry>,
222 entity_runtime_hooks: &'static [EntityRuntimeHooks<C>],
223 ) -> Self {
224 Self {
225 store,
226 entity_runtime_hooks,
227 _marker: PhantomData,
228 }
229 }
230
231 #[must_use]
232 pub(crate) const fn context<E>(&self) -> Context<'_, E>
233 where
234 E: EntityKind<Canister = C> + EntityValue,
235 {
236 Context::new(self)
237 }
238
239 pub(crate) fn recovered_context<E>(&self) -> Result<Context<'_, E>, InternalError>
244 where
245 E: EntityKind<Canister = C> + EntityValue,
246 {
247 ensure_recovered(self)?;
248
249 Ok(Context::new(self))
250 }
251
252 pub(crate) fn ensure_recovered_state(&self) -> Result<(), InternalError> {
254 ensure_recovered(self)
255 }
256
257 pub(crate) fn with_store_registry<R>(&self, f: impl FnOnce(&StoreRegistry) -> R) -> R {
258 self.store.with(|reg| f(reg))
259 }
260
261 pub fn storage_report(
262 &self,
263 name_to_path: &[(&'static str, &'static str)],
264 ) -> Result<StorageReport, InternalError> {
265 diagnostics::storage_report(self, name_to_path)
266 }
267
268 pub(in crate::db) fn prepare_row_commit_op(
269 &self,
270 op: &CommitRowOp,
271 ) -> Result<PreparedRowCommitOp, InternalError> {
272 let hooks = self.runtime_hook_for_entity_path(op.entity_path.as_str())?;
273
274 (hooks.prepare_row_commit)(self, op)
275 }
276
277 pub(in crate::db) fn replay_commit_marker_row_ops(
278 &self,
279 row_ops: &[CommitRowOp],
280 ) -> Result<(), InternalError> {
281 replay_commit_marker_row_ops(self, row_ops)
282 }
283
284 pub(in crate::db) fn rebuild_secondary_indexes_from_rows(&self) -> Result<(), InternalError> {
285 rebuild_secondary_indexes_from_rows(self)
286 }
287
288 pub(crate) fn validate_delete_strong_relations(
290 &self,
291 target_path: &str,
292 deleted_target_keys: &BTreeSet<RawDataKey>,
293 ) -> Result<(), InternalError> {
294 if deleted_target_keys.is_empty() {
295 return Ok(());
296 }
297
298 for hooks in self.entity_runtime_hooks {
299 (hooks.validate_delete_strong_relations)(self, target_path, deleted_target_keys)?;
300 }
301
302 Ok(())
303 }
304}
305
306pub struct EntityRuntimeHooks<C: CanisterKind> {
314 pub(crate) entity_name: &'static str,
315 pub(crate) entity_path: &'static str,
316 pub(in crate::db) prepare_row_commit:
317 fn(&Db<C>, &CommitRowOp) -> Result<PreparedRowCommitOp, InternalError>,
318 pub(crate) validate_delete_strong_relations: StrongRelationDeleteValidateFn<C>,
319}
320
321impl<C: CanisterKind> EntityRuntimeHooks<C> {
322 #[must_use]
323 pub(in crate::db) const fn new(
324 entity_name: &'static str,
325 entity_path: &'static str,
326 prepare_row_commit: fn(&Db<C>, &CommitRowOp) -> Result<PreparedRowCommitOp, InternalError>,
327 validate_delete_strong_relations: StrongRelationDeleteValidateFn<C>,
328 ) -> Self {
329 Self {
330 entity_name,
331 entity_path,
332 prepare_row_commit,
333 validate_delete_strong_relations,
334 }
335 }
336
337 #[must_use]
338 pub const fn for_entity<E>(
339 validate_delete_strong_relations: StrongRelationDeleteValidateFn<C>,
340 ) -> Self
341 where
342 E: EntityKind<Canister = C> + EntityValue,
343 {
344 Self::new(
345 <E as EntityIdentity>::ENTITY_NAME,
346 E::PATH,
347 prepare_row_commit_for_entity::<E>,
348 validate_delete_strong_relations,
349 )
350 }
351}
352
353impl<C: CanisterKind> Db<C> {
354 #[must_use]
355 pub(crate) const fn has_runtime_hooks(&self) -> bool {
356 !self.entity_runtime_hooks.is_empty()
357 }
358
359 pub(crate) fn runtime_hook_for_entity_name(
362 &self,
363 entity_name: &str,
364 ) -> Result<&EntityRuntimeHooks<C>, InternalError> {
365 let mut matched = None;
366 for hooks in self.entity_runtime_hooks {
367 if hooks.entity_name != entity_name {
368 continue;
369 }
370
371 if matched.is_some() {
372 return Err(InternalError::store_invariant(format!(
373 "duplicate runtime hooks for entity name '{entity_name}'"
374 )));
375 }
376
377 matched = Some(hooks);
378 }
379
380 matched.ok_or_else(|| {
381 InternalError::store_unsupported(format!(
382 "unsupported entity name in data store: '{entity_name}'"
383 ))
384 })
385 }
386
387 pub(crate) fn runtime_hook_for_entity_path(
390 &self,
391 entity_path: &str,
392 ) -> Result<&EntityRuntimeHooks<C>, InternalError> {
393 let mut matched = None;
394 for hooks in self.entity_runtime_hooks {
395 if hooks.entity_path != entity_path {
396 continue;
397 }
398
399 if matched.is_some() {
400 return Err(InternalError::store_invariant(format!(
401 "duplicate runtime hooks for entity path '{entity_path}'"
402 )));
403 }
404
405 matched = Some(hooks);
406 }
407
408 matched.ok_or_else(|| InternalError::unsupported_entity_path(entity_path))
409 }
410}
411
412impl<C: CanisterKind> Copy for Db<C> {}
413
414impl<C: CanisterKind> Clone for Db<C> {
415 fn clone(&self) -> Self {
416 *self
417 }
418}