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