1pub(crate) mod access;
8pub(crate) mod contracts;
9pub(crate) mod cursor;
10pub(crate) mod diagnostics;
11pub(crate) mod identity;
12pub(crate) mod predicate;
13pub(crate) mod query;
14pub(crate) mod registry;
15pub(crate) mod response;
16pub(crate) mod scalar_expr;
17pub(crate) mod schema;
18pub(crate) mod session;
19#[cfg(feature = "sql")]
20pub(crate) mod sql;
21
22pub(in crate::db) mod codec;
23pub(in crate::db) mod commit;
24pub(in crate::db) mod data;
25pub(in crate::db) mod direction;
26pub(in crate::db) mod executor;
27pub(in crate::db) mod index;
28pub(in crate::db) mod migration;
29pub(in crate::db) mod numeric;
30pub(in crate::db) mod reduced_sql;
31pub(in crate::db) mod relation;
32
33use crate::{
34 db::{
35 commit::{CommitRowOp, PreparedRowCommitOp, ensure_recovered},
36 data::RawDataKey,
37 executor::Context,
38 registry::StoreHandle,
39 relation::model_has_strong_relations_to_target,
40 },
41 error::InternalError,
42 traits::{CanisterKind, EntityKind, EntityValue},
43 types::EntityTag,
44};
45use std::{collections::BTreeSet, marker::PhantomData, thread::LocalKey};
46
47pub use codec::cursor::{decode_cursor, encode_cursor};
48pub use commit::EntityRuntimeHooks;
49pub use data::{
50 DataStore, PersistedRow, PersistedScalar, ScalarSlotValueRef, ScalarValueRef, SlotReader,
51 SlotWriter, UpdatePatch, decode_persisted_custom_many_slot_payload,
52 decode_persisted_custom_slot_payload, decode_persisted_non_null_slot_payload,
53 decode_persisted_option_scalar_slot_payload, decode_persisted_option_slot_payload,
54 decode_persisted_scalar_slot_payload, decode_persisted_slot_payload,
55 encode_persisted_custom_many_slot_payload, encode_persisted_custom_slot_payload,
56 encode_persisted_option_scalar_slot_payload, encode_persisted_scalar_slot_payload,
57 encode_persisted_slot_payload,
58};
59#[cfg(feature = "structural-read-metrics")]
60#[doc(hidden)]
61pub use data::{StructuralReadMetrics, with_structural_read_metrics};
62#[cfg(all(test, not(feature = "structural-read-metrics")))]
63#[expect(unused_imports)]
64pub(crate) use data::{StructuralReadMetrics, with_structural_read_metrics};
65pub use diagnostics::{
66 ExecutionAccessPathVariant, ExecutionMetrics, ExecutionOptimization, ExecutionTrace,
67 IntegrityReport, IntegrityStoreSnapshot, IntegrityTotals, StorageReport,
68};
69#[doc(hidden)]
70pub use executor::EntityAuthority;
71pub use executor::MutationMode;
72pub use executor::{ExecutionFamily, RouteExecutionMode};
73#[cfg(feature = "structural-read-metrics")]
74#[doc(hidden)]
75pub use executor::{GroupedCountFoldMetrics, with_grouped_count_fold_metrics};
76#[cfg(feature = "structural-read-metrics")]
77#[doc(hidden)]
78pub use executor::{RowCheckMetrics, with_row_check_metrics};
79#[cfg(all(test, not(feature = "structural-read-metrics")))]
80#[expect(unused_imports)]
81pub(crate) use executor::{RowCheckMetrics, with_row_check_metrics};
82pub use identity::{EntityName, IndexName};
83pub use index::{IndexState, IndexStore};
84pub use migration::{
85 MigrationCursor, MigrationPlan, MigrationRowOp, MigrationRunOutcome, MigrationRunState,
86 MigrationStep,
87};
88pub use predicate::{
89 CoercionId, CompareOp, ComparePredicate, MissingRowPolicy, Predicate, UnsupportedQueryFeature,
90};
91#[doc(hidden)]
92pub use predicate::{
93 parse_generated_index_predicate_sql, validate_generated_index_predicate_fields,
94};
95pub use query::{
96 api::ResponseCardinalityExt,
97 builder::{
98 AggregateExpr, FieldRef, avg, count, count_by, exists, first, last, max, max_by, min,
99 min_by, sum,
100 },
101 explain::{
102 ExplainAggregateTerminalPlan, ExplainExecutionDescriptor, ExplainExecutionMode,
103 ExplainExecutionNodeDescriptor, ExplainExecutionNodeType, ExplainExecutionOrderingSource,
104 ExplainPlan,
105 },
106 expr::{FilterExpr, SortExpr},
107 fluent::{
108 delete::FluentDeleteQuery,
109 load::{FluentLoadQuery, PagedLoadQuery},
110 },
111 intent::{CompiledQuery, IntentError, PlannedQuery, Query, QueryError, QueryExecutionError},
112 plan::{DeleteSpec, LoadSpec, OrderDirection, PlanError, QueryMode},
113 trace::{QueryTracePlan, TraceExecutionFamily},
114};
115pub use registry::StoreRegistry;
116pub(in crate::db) use response::GroupedTextCursorPageWithTrace;
117pub use response::{
118 EntityResponse, GroupedRow, PagedGroupedExecution, PagedGroupedExecutionWithTrace,
119 PagedLoadExecution, PagedLoadExecutionWithTrace, ProjectedRow, ProjectionResponse,
120 Response as RowResponse, ResponseError, ResponseRow, Row, WriteBatchResponse,
121};
122pub use schema::{
123 EntityFieldDescription, EntityIndexDescription, EntityRelationCardinality,
124 EntityRelationDescription, EntityRelationStrength, EntitySchemaDescription, ValidateError,
125};
126#[cfg(not(feature = "sql"))]
127pub use session::DbSession;
128#[cfg(feature = "sql")]
129pub use session::{
130 DbSession, SqlDispatchResult, SqlParsedStatement, SqlStatementRoute,
131 debug_mark_store_index_state, debug_remove_entity_row_data_only,
132};
133#[cfg(all(feature = "sql", feature = "perf-attribution"))]
134pub use session::{LoweredSqlDispatchExecutorAttribution, SqlProjectionTextExecutorAttribution};
135#[cfg(all(feature = "sql", feature = "structural-read-metrics"))]
136#[doc(hidden)]
137pub use session::{
138 SqlProjectionMaterializationMetrics, with_sql_projection_materialization_metrics,
139};
140#[cfg(feature = "sql")]
141pub use sql::identifier::{
142 identifier_last_segment, identifiers_tail_match, normalize_identifier_to_scope,
143 split_qualified_identifier,
144};
145#[cfg(feature = "sql")]
146pub use sql::lowering::LoweredSqlCommand;
147
148pub(crate) struct Db<C: CanisterKind> {
154 store: &'static LocalKey<StoreRegistry>,
155 entity_runtime_hooks: &'static [EntityRuntimeHooks<C>],
156 _marker: PhantomData<C>,
157}
158
159impl<C: CanisterKind> Db<C> {
160 #[must_use]
162 #[cfg(test)]
163 pub(crate) const fn new(store: &'static LocalKey<StoreRegistry>) -> Self {
164 Self::new_with_hooks(store, &[])
165 }
166
167 #[must_use]
169 pub(crate) const fn new_with_hooks(
170 store: &'static LocalKey<StoreRegistry>,
171 entity_runtime_hooks: &'static [EntityRuntimeHooks<C>],
172 ) -> Self {
173 #[cfg(debug_assertions)]
174 {
175 let _ = crate::db::commit::debug_assert_unique_runtime_hook_tags(entity_runtime_hooks);
176 }
177
178 Self {
179 store,
180 entity_runtime_hooks,
181 _marker: PhantomData,
182 }
183 }
184
185 #[must_use]
186 pub(in crate::db) const fn context<E>(&self) -> Context<'_, E>
187 where
188 E: EntityKind<Canister = C> + EntityValue,
189 {
190 Context::new(self)
191 }
192
193 pub(in crate::db) fn recovered_store(&self, path: &str) -> Result<StoreHandle, InternalError> {
195 ensure_recovered(self)?;
196
197 self.store_handle(path)
198 }
199
200 fn store_handle(&self, path: &str) -> Result<StoreHandle, InternalError> {
206 self.with_store_registry(|registry| registry.try_get_store(path))
207 }
208
209 pub(crate) fn ensure_recovered_state(&self) -> Result<(), InternalError> {
211 ensure_recovered(self)
212 }
213
214 pub(crate) fn with_store_registry<R>(&self, f: impl FnOnce(&StoreRegistry) -> R) -> R {
216 self.store.with(|reg| f(reg))
217 }
218
219 #[must_use]
221 pub(in crate::db) fn store_resolver(&self) -> executor::StoreResolver<'_> {
222 executor::StoreResolver::new(self)
223 }
224
225 pub(in crate::db) fn mark_all_registered_index_stores_ready(&self) {
230 self.with_store_registry(|registry| {
231 for (_, handle) in registry.iter() {
232 handle.mark_index_ready();
233 }
234 });
235 }
236
237 pub(crate) fn storage_report(
239 &self,
240 name_to_path: &[(&'static str, &'static str)],
241 ) -> Result<StorageReport, InternalError> {
242 diagnostics::storage_report(self, name_to_path)
243 }
244
245 pub(crate) fn storage_report_default(&self) -> Result<StorageReport, InternalError> {
247 diagnostics::storage_report_default(self)
248 }
249
250 pub(crate) fn integrity_report(&self) -> Result<IntegrityReport, InternalError> {
252 diagnostics::integrity_report(self)
253 }
254
255 pub(in crate::db) fn prepare_row_commit_op(
256 &self,
257 op: &CommitRowOp,
258 ) -> Result<PreparedRowCommitOp, InternalError> {
259 let hooks = self.runtime_hook_for_entity_path(op.entity_path.as_ref())?;
260 let store = self.store_handle(hooks.store_path)?;
261
262 (hooks.prepare_row_commit_with_readers)(self, op, &store, &store)
263 }
264
265 pub(crate) fn execute_migration_plan(
267 &self,
268 plan: &migration::MigrationPlan,
269 max_steps: usize,
270 ) -> Result<migration::MigrationRunOutcome, InternalError> {
271 migration::execute_migration_plan(self, plan, max_steps)
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() {
282 return Ok(());
283 }
284
285 for hooks in self.entity_runtime_hooks {
287 if !model_has_strong_relations_to_target(hooks.model, target_path) {
288 continue;
289 }
290
291 (hooks.validate_delete_strong_relations)(self, target_path, deleted_target_keys)?;
292 }
293
294 Ok(())
295 }
296}
297
298impl<C: CanisterKind> Db<C> {
299 #[must_use]
301 pub(crate) const fn has_runtime_hooks(&self) -> bool {
302 commit::has_runtime_hooks(self.entity_runtime_hooks)
303 }
304
305 #[must_use]
307 pub(crate) fn runtime_entity_names(&self) -> Vec<String> {
308 self.entity_runtime_hooks
309 .iter()
310 .map(|hooks| hooks.model.name().to_string())
311 .collect()
312 }
313
314 pub(crate) fn runtime_hook_for_entity_tag(
317 &self,
318 entity_tag: EntityTag,
319 ) -> Result<&EntityRuntimeHooks<C>, InternalError> {
320 commit::resolve_runtime_hook_by_tag(self.entity_runtime_hooks, entity_tag)
321 }
322
323 pub(crate) fn runtime_hook_for_entity_path(
326 &self,
327 entity_path: &str,
328 ) -> Result<&EntityRuntimeHooks<C>, InternalError> {
329 commit::resolve_runtime_hook_by_path(self.entity_runtime_hooks, entity_path)
330 }
331}
332
333impl<C: CanisterKind> Copy for Db<C> {}
334
335impl<C: CanisterKind> Clone for Db<C> {
336 fn clone(&self) -> Self {
337 *self
338 }
339}