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 predicate;
11pub(crate) mod query;
12pub(crate) mod registry;
13pub(crate) mod response;
14pub(crate) mod session;
15
16pub(in crate::db) mod codec;
17pub(in crate::db) mod commit;
18pub(in crate::db) mod data;
19pub(in crate::db) mod executor;
20pub(in crate::db) mod index;
21pub(in crate::db) mod lowering;
22pub(in crate::db) mod relation;
23pub(in crate::db) mod value_hash;
24
25pub use codec::cursor::{decode_cursor, encode_cursor};
27pub use consistency::ReadConsistency;
28pub use data::DataStore;
29pub(crate) use data::StorageKey;
30pub use diagnostics::StorageReport;
31pub use executor::{ExecutionAccessPathVariant, ExecutionOptimization, ExecutionTrace};
32pub use identity::{EntityName, IndexName};
33pub use index::IndexStore;
34pub use predicate::{CoercionId, CompareOp, ComparePredicate, Predicate, UnsupportedQueryFeature};
35pub use query::{
36 builder::field::FieldRef,
37 expr::{FilterExpr, SortExpr},
38 fluent::{
39 delete::FluentDeleteQuery,
40 load::{FluentLoadQuery, PagedLoadQuery},
41 },
42 intent::{IntentError, Query, QueryError},
43 plan::{OrderDirection, PlanError},
44 predicate::ValidateError,
45};
46pub use registry::StoreRegistry;
47pub use relation::validate_delete_strong_relations_for_source;
48pub use response::{Response, ResponseError, Row, WriteBatchResponse, WriteResponse};
49pub use session::DbSession;
50#[cfg(test)]
51pub(crate) use value_hash::hash_value;
52
53use crate::{
55 db::{
56 commit::{
57 CommitRowOp, PreparedRowCommitOp, ensure_recovered, prepare_row_commit_for_entity,
58 },
59 data::RawDataKey,
60 executor::Context,
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(crate) fn validate_delete_strong_relations(
283 &self,
284 target_path: &str,
285 deleted_target_keys: &BTreeSet<RawDataKey>,
286 ) -> Result<(), InternalError> {
287 if deleted_target_keys.is_empty() {
288 return Ok(());
289 }
290
291 for hooks in self.entity_runtime_hooks {
292 (hooks.validate_delete_strong_relations)(self, target_path, deleted_target_keys)?;
293 }
294
295 Ok(())
296 }
297}
298
299pub struct EntityRuntimeHooks<C: CanisterKind> {
307 pub(crate) entity_name: &'static str,
308 pub(crate) entity_path: &'static str,
309 pub(in crate::db) prepare_row_commit:
310 fn(&Db<C>, &CommitRowOp) -> Result<PreparedRowCommitOp, InternalError>,
311 pub(crate) validate_delete_strong_relations: StrongRelationDeleteValidateFn<C>,
312}
313
314impl<C: CanisterKind> EntityRuntimeHooks<C> {
315 #[must_use]
316 pub(in crate::db) const fn new(
317 entity_name: &'static str,
318 entity_path: &'static str,
319 prepare_row_commit: fn(&Db<C>, &CommitRowOp) -> Result<PreparedRowCommitOp, InternalError>,
320 validate_delete_strong_relations: StrongRelationDeleteValidateFn<C>,
321 ) -> Self {
322 Self {
323 entity_name,
324 entity_path,
325 prepare_row_commit,
326 validate_delete_strong_relations,
327 }
328 }
329
330 #[must_use]
331 pub const fn for_entity<E>(
332 validate_delete_strong_relations: StrongRelationDeleteValidateFn<C>,
333 ) -> Self
334 where
335 E: EntityKind<Canister = C> + EntityValue,
336 {
337 Self::new(
338 <E as EntityIdentity>::ENTITY_NAME,
339 E::PATH,
340 prepare_row_commit_for_entity::<E>,
341 validate_delete_strong_relations,
342 )
343 }
344}
345
346impl<C: CanisterKind> Db<C> {
347 #[must_use]
348 pub(crate) const fn has_runtime_hooks(&self) -> bool {
349 !self.entity_runtime_hooks.is_empty()
350 }
351
352 pub(crate) fn runtime_hook_for_entity_name(
355 &self,
356 entity_name: &str,
357 ) -> Result<&EntityRuntimeHooks<C>, InternalError> {
358 let mut matched = None;
359 for hooks in self.entity_runtime_hooks {
360 if hooks.entity_name != entity_name {
361 continue;
362 }
363
364 if matched.is_some() {
365 return Err(InternalError::store_invariant(format!(
366 "duplicate runtime hooks for entity name '{entity_name}'"
367 )));
368 }
369
370 matched = Some(hooks);
371 }
372
373 matched.ok_or_else(|| {
374 InternalError::store_unsupported(format!(
375 "unsupported entity name in data store: '{entity_name}'"
376 ))
377 })
378 }
379
380 pub(crate) fn runtime_hook_for_entity_path(
383 &self,
384 entity_path: &str,
385 ) -> Result<&EntityRuntimeHooks<C>, InternalError> {
386 let mut matched = None;
387 for hooks in self.entity_runtime_hooks {
388 if hooks.entity_path != entity_path {
389 continue;
390 }
391
392 if matched.is_some() {
393 return Err(InternalError::store_invariant(format!(
394 "duplicate runtime hooks for entity path '{entity_path}'"
395 )));
396 }
397
398 matched = Some(hooks);
399 }
400
401 matched.ok_or_else(|| InternalError::unsupported_entity_path(entity_path))
402 }
403}
404
405impl<C: CanisterKind> Copy for Db<C> {}
406
407impl<C: CanisterKind> Clone for Db<C> {
408 fn clone(&self) -> Self {
409 *self
410 }
411}