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
60pub type PagedLoadExecution<E> = (Response<E>, Option<Vec<u8>>);
66
67pub type PagedLoadExecutionWithTrace<E> = (Response<E>, Option<Vec<u8>>, Option<ExecutionTrace>);
73
74pub struct Db<C: CanisterKind> {
80 store: &'static LocalKey<StoreRegistry>,
81 entity_runtime_hooks: &'static [EntityRuntimeHooks<C>],
82 _marker: PhantomData<C>,
83}
84
85impl<C: CanisterKind> Db<C> {
86 #[must_use]
87 pub const fn new(store: &'static LocalKey<StoreRegistry>) -> Self {
88 Self::new_with_hooks(store, &[])
89 }
90
91 #[must_use]
92 pub const fn new_with_hooks(
93 store: &'static LocalKey<StoreRegistry>,
94 entity_runtime_hooks: &'static [EntityRuntimeHooks<C>],
95 ) -> Self {
96 Self {
97 store,
98 entity_runtime_hooks,
99 _marker: PhantomData,
100 }
101 }
102
103 #[must_use]
104 pub(crate) const fn context<E>(&self) -> Context<'_, E>
105 where
106 E: EntityKind<Canister = C> + EntityValue,
107 {
108 Context::new(self)
109 }
110
111 pub(crate) fn recovered_context<E>(&self) -> Result<Context<'_, E>, InternalError>
116 where
117 E: EntityKind<Canister = C> + EntityValue,
118 {
119 ensure_recovered(self)?;
120
121 Ok(Context::new(self))
122 }
123
124 pub(crate) fn ensure_recovered_state(&self) -> Result<(), InternalError> {
126 ensure_recovered(self)
127 }
128
129 pub(crate) fn with_store_registry<R>(&self, f: impl FnOnce(&StoreRegistry) -> R) -> R {
130 self.store.with(|reg| f(reg))
131 }
132
133 pub fn storage_report(
134 &self,
135 name_to_path: &[(&'static str, &'static str)],
136 ) -> Result<StorageReport, InternalError> {
137 diagnostics::storage_report(self, name_to_path)
138 }
139
140 pub(in crate::db) fn prepare_row_commit_op(
141 &self,
142 op: &CommitRowOp,
143 ) -> Result<PreparedRowCommitOp, InternalError> {
144 let hooks = self
145 .entity_runtime_hooks
146 .iter()
147 .find(|hooks| hooks.entity_path == op.entity_path.as_str())
148 .ok_or_else(|| InternalError::unsupported_entity_path(op.entity_path.as_str()))?;
149
150 (hooks.prepare_row_commit)(self, op)
151 }
152
153 pub(crate) fn validate_delete_strong_relations(
155 &self,
156 target_path: &str,
157 deleted_target_keys: &BTreeSet<RawDataKey>,
158 ) -> Result<(), InternalError> {
159 if deleted_target_keys.is_empty() {
160 return Ok(());
161 }
162
163 for hooks in self.entity_runtime_hooks {
164 (hooks.validate_delete_strong_relations)(self, target_path, deleted_target_keys)?;
165 }
166
167 Ok(())
168 }
169}
170
171pub struct EntityRuntimeHooks<C: CanisterKind> {
179 pub(crate) entity_name: &'static str,
180 pub(crate) entity_path: &'static str,
181 pub(in crate::db) prepare_row_commit:
182 fn(&Db<C>, &CommitRowOp) -> Result<PreparedRowCommitOp, InternalError>,
183 pub(crate) validate_delete_strong_relations: StrongRelationDeleteValidateFn<C>,
184}
185
186impl<C: CanisterKind> EntityRuntimeHooks<C> {
187 #[must_use]
188 pub(in crate::db) const fn new(
189 entity_name: &'static str,
190 entity_path: &'static str,
191 prepare_row_commit: fn(&Db<C>, &CommitRowOp) -> Result<PreparedRowCommitOp, InternalError>,
192 validate_delete_strong_relations: StrongRelationDeleteValidateFn<C>,
193 ) -> Self {
194 Self {
195 entity_name,
196 entity_path,
197 prepare_row_commit,
198 validate_delete_strong_relations,
199 }
200 }
201
202 #[must_use]
203 pub const fn for_entity<E>(
204 validate_delete_strong_relations: StrongRelationDeleteValidateFn<C>,
205 ) -> Self
206 where
207 E: EntityKind<Canister = C> + EntityValue,
208 {
209 Self::new(
210 <E as EntityIdentity>::ENTITY_NAME,
211 E::PATH,
212 prepare_row_commit_for_entity::<E>,
213 validate_delete_strong_relations,
214 )
215 }
216}
217
218impl<C: CanisterKind> Db<C> {
219 #[must_use]
220 pub(crate) const fn has_runtime_hooks(&self) -> bool {
221 !self.entity_runtime_hooks.is_empty()
222 }
223
224 pub(crate) fn runtime_hook_for_entity_name(
225 &self,
226 entity_name: &str,
227 ) -> Result<&EntityRuntimeHooks<C>, InternalError> {
228 let mut matched = None;
229 for hooks in self.entity_runtime_hooks {
230 if hooks.entity_name != entity_name {
231 continue;
232 }
233
234 if matched.is_some() {
235 return Err(InternalError::store_invariant(format!(
236 "duplicate runtime hooks for entity name '{entity_name}'"
237 )));
238 }
239
240 matched = Some(hooks);
241 }
242
243 matched.ok_or_else(|| {
244 InternalError::store_unsupported(format!(
245 "unsupported entity name in data store: '{entity_name}'"
246 ))
247 })
248 }
249}
250
251impl<C: CanisterKind> Copy for Db<C> {}
252
253impl<C: CanisterKind> Clone for Db<C> {
254 fn clone(&self) -> Self {
255 *self
256 }
257}