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