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