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