1pub(crate) mod access;
8pub(crate) mod cursor;
9pub(crate) mod diagnostics;
10pub(crate) mod identity;
11#[cfg(feature = "diagnostics")]
12pub(crate) mod physical_access;
13pub(crate) mod predicate;
14pub(crate) mod query;
15pub(crate) mod registry;
16pub(crate) mod response;
17pub(crate) mod runtime_hooks;
18pub(crate) mod scalar_expr;
19pub(crate) mod schema;
20pub(crate) mod session;
21#[cfg(feature = "sql")]
22pub(crate) mod sql;
23
24pub(in crate::db) mod codec;
25pub(in crate::db) mod commit;
26pub(in crate::db) mod data;
27pub(in crate::db) mod direction;
28pub(in crate::db) mod executor;
29pub(in crate::db) mod index;
30pub(in crate::db) mod key_taxonomy;
31pub(in crate::db) mod numeric;
32pub(in crate::db) mod relation;
33pub(in crate::db) mod sql_shared;
34#[cfg(test)]
35mod tests;
36
37use crate::{
38 db::{
39 commit::{CommitRowOp, PreparedRowCommitOp, ensure_recovered},
40 data::RawDataStoreKey,
41 executor::Context,
42 registry::StoreHandle,
43 },
44 error::InternalError,
45 traits::{CanisterKind, EntityKind, EntityValue},
46 types::EntityTag,
47};
48use std::{collections::BTreeSet, marker::PhantomData, thread::LocalKey};
49
50#[doc(hidden)]
51pub use codec::hex::encode_hex_lower;
52pub use cursor::{decode_cursor, encode_cursor};
53pub use runtime_hooks::EntityRuntimeHooks;
54pub use data::{DataStore, PersistedRow, SlotReader, SlotWriter, StructuralPatch};
58#[doc(hidden)]
59pub use data::{
60 PersistedScalar, ScalarSlotValueRef, ScalarValueRef,
61 decode_persisted_many_slot_payload_by_meta, decode_persisted_option_scalar_slot_payload,
62 decode_persisted_option_slot_payload_by_kind, decode_persisted_option_slot_payload_by_meta,
63 decode_persisted_scalar_slot_payload, decode_persisted_slot_payload_by_kind,
64 decode_persisted_slot_payload_by_meta, decode_persisted_structured_many_slot_payload,
65 decode_persisted_structured_slot_payload, decode_slot_into_runtime_value,
66 encode_persisted_many_slot_payload_by_meta, encode_persisted_option_scalar_slot_payload,
67 encode_persisted_option_slot_payload_by_meta, encode_persisted_scalar_slot_payload,
68 encode_persisted_slot_payload_by_kind, encode_persisted_slot_payload_by_meta,
69 encode_persisted_structured_many_slot_payload, encode_persisted_structured_slot_payload,
70 encode_runtime_value_into_slot,
71};
72#[cfg(feature = "diagnostics")]
73#[doc(hidden)]
74pub use data::{StructuralReadMetrics, with_structural_read_metrics};
75#[cfg(all(test, not(feature = "diagnostics")))]
76#[expect(unused_imports)]
77pub(crate) use data::{StructuralReadMetrics, with_structural_read_metrics};
78pub use diagnostics::{
79 DataStoreSnapshot, EntitySnapshot, ExecutionAccessPathVariant, ExecutionMetrics,
80 ExecutionOptimization, ExecutionStats, ExecutionTrace, IndexStoreSnapshot, IntegrityReport,
81 IntegrityStoreSnapshot, IntegrityTotals, StorageReport,
82};
83#[doc(hidden)]
84pub use executor::EntityAuthority;
85pub use executor::MutationMode;
86pub use executor::{ExecutionFamily, RouteExecutionMode};
87#[cfg(feature = "diagnostics")]
88#[doc(hidden)]
89pub use executor::{RowCheckMetrics, with_row_check_metrics};
90#[cfg(all(test, not(feature = "diagnostics")))]
91#[expect(unused_imports)]
92pub(crate) use executor::{RowCheckMetrics, with_row_check_metrics};
93#[cfg(feature = "diagnostics")]
94#[doc(hidden)]
95pub use executor::{ScalarMaterializationLaneMetrics, with_scalar_materialization_lane_metrics};
96#[cfg(all(test, not(feature = "diagnostics")))]
97#[expect(unused_imports)]
98pub(crate) use executor::{
99 ScalarMaterializationLaneMetrics, with_scalar_materialization_lane_metrics,
100};
101pub use identity::{EntityName, IndexName};
102pub use index::{IndexState, IndexStore};
103#[doc(hidden)]
104pub use key_taxonomy::{
105 CompositePrimaryKeyValue, CompositePrimaryKeyValueError, PrimaryKeyComponent, PrimaryKeyValue,
106};
107pub use predicate::{
108 CoercionId, CompareFieldsPredicate, CompareOp, ComparePredicate, MissingRowPolicy, Predicate,
109 UnsupportedQueryFeature,
110};
111#[doc(hidden)]
112pub use predicate::{
113 parse_generated_index_predicate_sql, validate_generated_index_predicate_fields,
114};
115pub use query::{
116 api::ResponseCardinalityExt,
117 builder::{
118 AggregateExpr, FieldRef, NumericProjectionExpr, RoundProjectionExpr, TextProjectionExpr,
119 ValueProjectionExpr, add, avg, contains, count, count_by, div, ends_with, exists, first,
120 last, left, length, lower, ltrim, max, max_by, min, min_by, mul, position, replace, right,
121 round, round_expr, rtrim, starts_with, sub, substring, substring_with_length, sum, trim,
122 upper,
123 },
124 explain::{
125 ExplainAccessCandidateV1, ExplainAccessDecisionKind, ExplainAccessDecisionV1,
126 ExplainAggregateTerminalPlan, ExplainEligibleAlternativeV1, ExplainExecutionDescriptor,
127 ExplainExecutionMode, ExplainExecutionNodeDescriptor, ExplainExecutionNodeType,
128 ExplainExecutionOrderingSource, ExplainPlan, ExplainRejectedIndexV1,
129 ExplainResidualSummaryV1, ExplainSelectedAccessV1,
130 },
131 expr::{FilterExpr, FilterValue, OrderExpr, OrderTerm, asc, desc, field},
132 fluent::{
133 delete::FluentDeleteQuery,
134 load::{FluentLoadQuery, LoadQueryResult, PagedLoadQuery},
135 },
136 intent::{
137 AccessRequirementError, AccessRequirementViolation, CompiledQuery, IntentError,
138 PlannedQuery, Query, QueryError, QueryExecutionError, RequiredAccessPath,
139 },
140 plan::{DeleteSpec, LoadSpec, OrderDirection, PlanError, QueryMode},
141 trace::{QueryTracePlan, TraceExecutionFamily, TraceReuseArtifactClass, TraceReuseEvent},
142};
143pub use registry::StoreRegistry;
144pub use response::{
145 EntityResponse, GroupedRow, PagedGroupedExecution, PagedGroupedExecutionWithTrace,
146 PagedLoadExecution, PagedLoadExecutionWithTrace, ProjectedRow, ProjectionResponse,
147 Response as RowResponse, ResponseError, ResponseRow, Row, WriteBatchResponse,
148};
149pub use schema::{
150 EntityFieldDescription, EntityIndexDescription, EntityRelationCardinality,
151 EntityRelationDescription, EntityRelationStrength, EntitySchemaCheckDescription,
152 EntitySchemaDescription, SchemaStore, ValidateError,
153};
154#[cfg(not(feature = "sql"))]
155pub use session::DbSession;
156#[cfg(feature = "sql")]
157pub use session::{
158 DbSession, SqlDdlExecutionStatus, SqlDdlMutationKind, SqlDdlPreparationReport,
159 SqlStatementResult, SqlStatementSurface, sql_statement_entity_name, sql_statement_surface,
160};
161#[cfg(feature = "diagnostics")]
162pub use session::{
163 DirectDataRowAttribution, GroupedCountAttribution, GroupedExecutionAttribution,
164 QueryExecutionAttribution,
165};
166#[cfg(all(feature = "sql", feature = "diagnostics"))]
167pub use session::{
168 SqlCompileAttribution, SqlExecutionAttribution, SqlPureCoveringAttribution,
169 SqlQueryCacheAttribution, SqlQueryExecutionAttribution, SqlScalarAggregateAttribution,
170};
171#[cfg(all(feature = "sql", feature = "diagnostics"))]
172#[doc(hidden)]
173pub use session::{
174 SqlProjectionMaterializationMetrics, with_sql_projection_materialization_metrics,
175};
176#[cfg(feature = "sql")]
177pub use sql::identifier::{
178 identifier_last_segment, identifiers_tail_match, normalize_identifier_to_scope,
179 split_qualified_identifier,
180};
181#[cfg(feature = "sql")]
182pub use sql::lowering::LoweredSqlCommand;
183
184#[doc(hidden)]
186pub type GeneratedStructuralMapPayloadSlices<'a> = Vec<(&'a [u8], &'a [u8])>;
187
188#[doc(hidden)]
190pub type GeneratedStructuralEnumPayload<'a> = (String, Option<String>, Option<&'a [u8]>);
191
192#[doc(hidden)]
194#[must_use]
195pub(crate) fn encode_generated_structural_text_payload_bytes(value: &str) -> Vec<u8> {
196 data::encode_value_storage_text(value)
197}
198
199#[doc(hidden)]
201#[must_use]
202pub(crate) fn encode_generated_structural_list_payload_bytes(items: &[&[u8]]) -> Vec<u8> {
203 data::encode_value_storage_list_item_slices(items)
204}
205
206#[doc(hidden)]
208#[must_use]
209pub(crate) fn encode_generated_structural_map_payload_bytes(entries: &[(&[u8], &[u8])]) -> Vec<u8> {
210 data::encode_value_storage_map_entry_slices(entries)
211}
212
213#[doc(hidden)]
215#[must_use]
216pub(crate) fn encode_generated_structural_enum_payload_bytes(
217 variant: &str,
218 path: Option<&str>,
219 payload: Option<&[u8]>,
220) -> Vec<u8> {
221 data::encode_enum(variant, path, payload)
222}
223
224#[doc(hidden)]
226pub(crate) fn decode_generated_structural_text_payload_bytes(
227 raw_bytes: &[u8],
228) -> Result<String, InternalError> {
229 data::decode_value_storage_text(raw_bytes).map_err(InternalError::persisted_row_decode_failed)
230}
231
232#[doc(hidden)]
234pub(crate) fn decode_generated_structural_list_payload_bytes(
235 raw_bytes: &[u8],
236) -> Result<Vec<&[u8]>, InternalError> {
237 data::decode_value_storage_list_item_slices(raw_bytes)
238 .map_err(InternalError::persisted_row_decode_failed)
239}
240
241#[doc(hidden)]
243pub(crate) fn decode_generated_structural_map_payload_bytes(
244 raw_bytes: &[u8],
245) -> Result<GeneratedStructuralMapPayloadSlices<'_>, InternalError> {
246 data::decode_value_storage_map_entry_slices(raw_bytes)
247 .map_err(InternalError::persisted_row_decode_failed)
248}
249
250#[doc(hidden)]
252pub(crate) fn decode_generated_structural_enum_payload_bytes(
253 raw_bytes: &[u8],
254) -> Result<GeneratedStructuralEnumPayload<'_>, InternalError> {
255 data::decode_enum(raw_bytes).map_err(InternalError::persisted_row_decode_failed)
256}
257
258#[doc(hidden)]
260pub(crate) fn generated_persisted_structured_payload_decode_failed(
261 detail: impl std::fmt::Display,
262) -> InternalError {
263 InternalError::persisted_row_decode_failed(detail)
264}
265
266pub(crate) struct Db<C: CanisterKind> {
272 store: &'static LocalKey<StoreRegistry>,
273 entity_runtime_hooks: &'static [EntityRuntimeHooks<C>],
274 _marker: PhantomData<C>,
275}
276
277impl<C: CanisterKind> Db<C> {
278 #[must_use]
280 #[cfg(test)]
281 pub(crate) const fn new(store: &'static LocalKey<StoreRegistry>) -> Self {
282 Self::new_with_hooks(store, &[])
283 }
284
285 #[must_use]
287 pub(crate) const fn new_with_hooks(
288 store: &'static LocalKey<StoreRegistry>,
289 entity_runtime_hooks: &'static [EntityRuntimeHooks<C>],
290 ) -> Self {
291 #[cfg(debug_assertions)]
292 {
293 let _ = crate::db::runtime_hooks::debug_assert_unique_runtime_hook_tags(
294 entity_runtime_hooks,
295 );
296 }
297
298 Self {
299 store,
300 entity_runtime_hooks,
301 _marker: PhantomData,
302 }
303 }
304
305 #[must_use]
306 pub(in crate::db) const fn context<E>(&self) -> Context<'_, E>
307 where
308 E: EntityKind<Canister = C> + EntityValue,
309 {
310 Context::new(self)
311 }
312
313 pub(in crate::db) fn recovered_store(&self, path: &str) -> Result<StoreHandle, InternalError> {
315 ensure_recovered(self)?;
316
317 self.store_handle(path)
318 }
319
320 pub(in crate::db) fn store_handle(&self, path: &str) -> Result<StoreHandle, InternalError> {
326 self.with_store_registry(|registry| registry.try_get_store(path))
327 }
328
329 pub(crate) fn ensure_recovered_state(&self) -> Result<(), InternalError> {
331 ensure_recovered(self)
332 }
333
334 pub(crate) fn with_store_registry<R>(&self, f: impl FnOnce(&StoreRegistry) -> R) -> R {
336 self.store.with(|reg| f(reg))
337 }
338
339 #[must_use]
346 pub(in crate::db) fn cache_scope_id(&self) -> usize {
347 std::ptr::from_ref::<LocalKey<StoreRegistry>>(self.store) as usize
348 }
349
350 #[must_use]
352 pub(in crate::db) fn store_resolver(&self) -> executor::StoreResolver<'_> {
353 executor::StoreResolver::new(self)
354 }
355
356 pub(in crate::db) fn mark_all_registered_index_stores_ready(&self) {
361 self.with_store_registry(|registry| {
362 for (_, handle) in registry.iter() {
363 handle.mark_index_ready();
364 }
365 });
366 }
367
368 pub(crate) fn storage_report(
370 &self,
371 name_to_path: &[(&'static str, &'static str)],
372 ) -> Result<StorageReport, InternalError> {
373 diagnostics::storage_report(self, name_to_path)
374 }
375
376 pub(crate) fn storage_report_default(&self) -> Result<StorageReport, InternalError> {
378 diagnostics::storage_report_default(self)
379 }
380
381 pub(crate) fn integrity_report(&self) -> Result<IntegrityReport, InternalError> {
383 diagnostics::integrity_report(self)
384 }
385
386 pub(in crate::db) fn prepare_row_commit_op(
387 &self,
388 op: &CommitRowOp,
389 ) -> Result<PreparedRowCommitOp, InternalError> {
390 runtime_hooks::prepare_row_commit_with_hook(self, self.entity_runtime_hooks, op)
391 }
392
393 pub(crate) fn validate_delete_strong_relations(
395 &self,
396 target_path: &str,
397 deleted_target_keys: &BTreeSet<RawDataStoreKey>,
398 ) -> Result<(), InternalError> {
399 runtime_hooks::validate_delete_strong_relations_with_hooks(
400 self,
401 self.entity_runtime_hooks,
402 target_path,
403 deleted_target_keys,
404 )
405 }
406}
407
408impl<C: CanisterKind> Db<C> {
409 #[must_use]
411 pub(crate) const fn has_runtime_hooks(&self) -> bool {
412 runtime_hooks::has_runtime_hooks(self.entity_runtime_hooks)
413 }
414
415 #[must_use]
417 pub(crate) fn runtime_entity_names(&self) -> Vec<String> {
418 self.entity_runtime_hooks
419 .iter()
420 .map(|hooks| hooks.model.name().to_string())
421 .collect()
422 }
423
424 pub(crate) fn runtime_hook_for_entity_tag(
427 &self,
428 entity_tag: EntityTag,
429 ) -> Result<&EntityRuntimeHooks<C>, InternalError> {
430 runtime_hooks::resolve_runtime_hook_by_tag(self.entity_runtime_hooks, entity_tag)
431 }
432
433 pub(crate) fn runtime_hook_for_entity_path(
436 &self,
437 entity_path: &str,
438 ) -> Result<&EntityRuntimeHooks<C>, InternalError> {
439 runtime_hooks::resolve_runtime_hook_by_path(self.entity_runtime_hooks, entity_path)
440 }
441}
442
443impl<C: CanisterKind> Copy for Db<C> {}
444
445impl<C: CanisterKind> Clone for Db<C> {
446 fn clone(&self) -> Self {
447 *self
448 }
449}