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