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