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