1mod query;
7mod response;
8#[cfg(feature = "sql")]
9mod sql;
10#[cfg(all(test, feature = "sql"))]
14mod tests;
15mod write;
16
17#[cfg(feature = "sql")]
18use crate::db::{IndexState, QueryError, query::plan::VisibleIndexes};
19use crate::{
20 db::{
21 Db, EntityFieldDescription, EntityRuntimeHooks, EntitySchemaDescription, FluentDeleteQuery,
22 FluentLoadQuery, IntegrityReport, MissingRowPolicy, PersistedRow, Query, StorageReport,
23 StoreCatalogDescription, StoreRegistry, WriteBatchResponse,
24 commit::CommitSchemaFingerprint,
25 executor::{DeleteExecutor, EntityAuthority, LoadExecutor, SaveExecutor},
26 schema::{
27 AcceptedCatalogIdentity, AcceptedCatalogSnapshotSelection, AcceptedRowDecodeContract,
28 AcceptedRowLayoutRuntimeContract, AcceptedSchemaSnapshot, SchemaInfo, SchemaVersion,
29 accepted_commit_schema_fingerprint, accepted_schema_cache_fingerprint,
30 describe_entity_fields, describe_entity_fields_with_persisted_schema,
31 describe_entity_model, describe_entity_model_with_persisted_schema,
32 ensure_accepted_schema_snapshot, show_indexes_for_model,
33 show_indexes_for_model_with_runtime_state,
34 show_indexes_for_schema_info_with_runtime_state,
35 },
36 },
37 error::InternalError,
38 metrics::sink::{ExecKind, MetricsSink, record_exec_error_for_path, with_metrics_sink},
39 model::entity::EntityModel,
40 traits::{CanisterKind, EntityKind, EntityValue, Path},
41 value::Value,
42};
43use std::{
44 cell::{OnceCell, RefCell},
45 collections::HashMap,
46 thread::LocalKey,
47};
48
49#[cfg(feature = "diagnostics")]
50pub use query::{
51 DirectDataRowAttribution, GroupedCountAttribution, GroupedExecutionAttribution,
52 KernelRowAttribution, QueryExecutionAttribution,
53};
54pub(in crate::db) use response::finalize_scalar_paged_execution;
55pub(in crate::db) use response::finalize_structural_grouped_projection_result;
56#[cfg(feature = "sql")]
57pub(in crate::db) use response::sql_grouped_cursor_from_bytes;
58#[cfg(feature = "sql")]
59pub use sql::{
60 SqlAdminBulkUpdatePlan, SqlDdlExecutionStatus, SqlDdlMutationKind, SqlDdlPreparationReport,
61 SqlPublicBoundedUpdatePlan, SqlPublicPrimaryKeyUpdatePlan, SqlSessionCurrentUpdatePlan,
62 SqlStatementDispatch, SqlStatementResult, SqlStatementShellSurface, SqlStatementSurface,
63 SqlUpdateAssignmentPolicy, SqlUpdateExposurePolicy, SqlUpdateOrderPolicy,
64 SqlUpdatePolicyContext, SqlUpdatePolicyRejection, SqlUpdatePolicyReport,
65 SqlUpdateReturningBounds, SqlUpdateReturningPolicy, SqlUpdateStatementClassification,
66 SqlUpdateWherePolicy, SqlValidatedUpdatePlan, classify_sql_update_policy,
67 sql_statement_dispatch, sql_statement_entity_name, sql_statement_shell_surface,
68 sql_statement_surface,
69};
70#[cfg(all(feature = "sql", feature = "diagnostics"))]
71pub use sql::{
72 SqlCompileAttribution, SqlExecutionAttribution, SqlOutputBlobAttribution,
73 SqlPureCoveringAttribution, SqlQueryCacheAttribution, SqlQueryExecutionAttribution,
74 SqlScalarAggregateAttribution,
75};
76#[cfg(all(feature = "sql", feature = "diagnostics"))]
77pub use sql::{SqlProjectionMaterializationMetrics, with_sql_projection_materialization_metrics};
78
79pub struct DbSession<C: CanisterKind> {
86 db: Db<C>,
87 debug: bool,
88 metrics: Option<&'static dyn MetricsSink>,
89}
90
91#[cfg(test)]
92#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
93pub(in crate::db) struct AcceptedCatalogRuntimeCounterSnapshot {
94 pub schema_info_projections: u64,
95 pub persisted_schema_decodes: u64,
96 pub generated_compatible_row_layout_proofs: u64,
97 pub latest_by_entity_calls: u64,
98 pub visible_index_projections: u64,
99}
100
101#[derive(Clone, Debug)]
102struct AcceptedSchemaQueryCacheEntry {
103 snapshot: AcceptedSchemaSnapshot,
104 identity: AcceptedCatalogIdentity,
105}
106
107pub(in crate::db) type AcceptedSaveContract = (
108 AcceptedRowDecodeContract,
109 AcceptedRowDecodeContract,
110 SchemaInfo,
111 CommitSchemaFingerprint,
112);
113
114#[derive(Clone, Debug)]
115pub(in crate::db) struct AcceptedSchemaCatalogContext {
116 snapshot: AcceptedSchemaSnapshot,
117 identity: AcceptedCatalogIdentity,
118 schema_info: OnceCell<SchemaInfo>,
119}
120
121impl AcceptedSchemaCatalogContext {
122 #[must_use]
123 pub(in crate::db) const fn new(
124 snapshot: AcceptedSchemaSnapshot,
125 identity: AcceptedCatalogIdentity,
126 ) -> Self {
127 Self {
128 snapshot,
129 identity,
130 schema_info: OnceCell::new(),
131 }
132 }
133
134 #[must_use]
135 pub(in crate::db) const fn snapshot(&self) -> &AcceptedSchemaSnapshot {
136 &self.snapshot
137 }
138
139 #[must_use]
140 pub(in crate::db) const fn schema_version(&self) -> SchemaVersion {
141 self.identity.accepted_schema_version()
142 }
143
144 #[must_use]
145 pub(in crate::db) const fn fingerprint(&self) -> CommitSchemaFingerprint {
146 self.identity.accepted_schema_fingerprint()
147 }
148
149 #[must_use]
150 pub(in crate::db) const fn fingerprint_method_version(&self) -> u8 {
151 self.identity.fingerprint_method_version()
152 }
153
154 #[must_use]
155 #[cfg(feature = "sql")]
156 pub(in crate::db) const fn identity(&self) -> AcceptedCatalogIdentity {
157 self.identity
158 }
159
160 fn debug_assert_matches_entity<E>(&self)
161 where
162 E: EntityKind,
163 {
164 debug_assert_eq!(self.identity.entity_tag(), E::ENTITY_TAG);
165 debug_assert_eq!(self.identity.entity_path(), E::PATH);
166 debug_assert_eq!(self.identity.store_path(), E::Store::PATH);
167 }
168
169 pub(in crate::db) fn accepted_entity_authority_for<E>(
170 &self,
171 ) -> Result<EntityAuthority, InternalError>
172 where
173 E: EntityKind,
174 {
175 self.debug_assert_matches_entity::<E>();
176 let authority = EntityAuthority::new(E::MODEL, E::ENTITY_TAG, E::Store::PATH);
177 let (accepted_row_layout, row_proof) =
178 AcceptedRowLayoutRuntimeContract::from_generated_compatible_schema(
179 &self.snapshot,
180 authority.model(),
181 )?;
182 let row_decode_contract = accepted_row_layout.row_decode_contract();
183
184 Ok(authority.with_accepted_row_decode_contract(
185 row_proof,
186 row_decode_contract,
187 self.accepted_schema_info_for::<E>(),
188 ))
189 }
190
191 #[must_use]
192 pub(in crate::db) fn accepted_schema_info_for<E>(&self) -> SchemaInfo
193 where
194 E: EntityKind,
195 {
196 self.debug_assert_matches_entity::<E>();
197 self.schema_info
198 .get_or_init(|| {
199 SchemaInfo::from_accepted_snapshot_for_model_with_expression_indexes(
200 E::MODEL,
201 &self.snapshot,
202 true,
203 )
204 })
205 .clone()
206 }
207}
208
209pub(in crate::db) fn accepted_save_contract_for_descriptor<E>(
210 accepted_schema: &AcceptedSchemaSnapshot,
211 descriptor: &AcceptedRowLayoutRuntimeContract<'_>,
212) -> Result<AcceptedSaveContract, InternalError>
213where
214 E: EntityKind,
215{
216 let row_decode_contract = descriptor.row_decode_contract();
217 let mutation_row_decode_contract = row_decode_contract.clone();
218 let schema_info = SchemaInfo::from_accepted_snapshot_for_model(E::MODEL, accepted_schema);
219 let schema_fingerprint = accepted_commit_schema_fingerprint(accepted_schema)?;
220
221 Ok((
222 row_decode_contract,
223 mutation_row_decode_contract,
224 schema_info,
225 schema_fingerprint,
226 ))
227}
228
229thread_local! {
230 static ACCEPTED_SCHEMA_QUERY_CACHES: RefCell<HashMap<(usize, &'static str), AcceptedSchemaQueryCacheEntry>> =
235 RefCell::new(HashMap::default());
236}
237
238impl<C: CanisterKind> DbSession<C> {
239 #[must_use]
241 pub(crate) const fn new(db: Db<C>) -> Self {
242 Self {
243 db,
244 debug: false,
245 metrics: None,
246 }
247 }
248
249 #[must_use]
251 pub const fn new_with_hooks(
252 store: &'static LocalKey<StoreRegistry>,
253 entity_runtime_hooks: &'static [EntityRuntimeHooks<C>],
254 ) -> Self {
255 Self::new(Db::new_with_hooks(store, entity_runtime_hooks))
256 }
257
258 #[cfg(test)]
259 pub(in crate::db) fn reset_accepted_catalog_runtime_counters_for_tests() {
260 crate::db::schema::reset_accepted_schema_info_projection_count_for_tests();
261 crate::db::schema::reset_persisted_schema_snapshot_decode_count_for_tests();
262 crate::db::schema::reset_generated_compatible_row_layout_proof_count_for_tests();
263 crate::db::schema::reset_latest_raw_snapshots_by_entity_call_count_for_tests();
264 query::reset_visible_index_projection_count_for_tests();
265 }
266
267 #[cfg(test)]
268 pub(in crate::db) fn accepted_catalog_runtime_counter_snapshot_for_tests()
269 -> AcceptedCatalogRuntimeCounterSnapshot {
270 AcceptedCatalogRuntimeCounterSnapshot {
271 schema_info_projections:
272 crate::db::schema::accepted_schema_info_projection_count_for_tests(),
273 persisted_schema_decodes:
274 crate::db::schema::persisted_schema_snapshot_decode_count_for_tests(),
275 generated_compatible_row_layout_proofs:
276 crate::db::schema::generated_compatible_row_layout_proof_count_for_tests(),
277 latest_by_entity_calls:
278 crate::db::schema::latest_raw_snapshots_by_entity_call_count_for_tests(),
279 visible_index_projections: query::visible_index_projection_count_for_tests(),
280 }
281 }
282
283 #[must_use]
285 pub const fn debug(mut self) -> Self {
286 self.debug = true;
287 self
288 }
289
290 #[must_use]
292 pub const fn metrics_sink(mut self, sink: &'static dyn MetricsSink) -> Self {
293 self.metrics = Some(sink);
294 self
295 }
296
297 const fn fluent_load_query<E>(&self, consistency: MissingRowPolicy) -> FluentLoadQuery<'_, E>
300 where
301 E: EntityKind<Canister = C>,
302 {
303 FluentLoadQuery::new(self, Query::new(consistency))
304 }
305
306 fn fluent_delete_query<E>(&self, consistency: MissingRowPolicy) -> FluentDeleteQuery<'_, E>
310 where
311 E: PersistedRow<Canister = C>,
312 {
313 FluentDeleteQuery::new(self, Query::new(consistency).delete())
314 }
315
316 fn with_metrics<T>(&self, f: impl FnOnce() -> T) -> T {
317 if let Some(sink) = self.metrics {
318 with_metrics_sink(sink, f)
319 } else {
320 f()
321 }
322 }
323
324 fn execute_save_with<E, T, R>(
326 &self,
327 op: impl FnOnce(SaveExecutor<E>) -> Result<T, InternalError>,
328 map: impl FnOnce(T) -> R,
329 ) -> Result<R, InternalError>
330 where
331 E: PersistedRow<Canister = C> + EntityValue,
332 {
333 let (contract, schema_info, schema_fingerprint) = match self
334 .with_metrics(|| self.ensure_generated_compatible_accepted_save_schema::<E>())
335 {
336 Ok(authority) => authority,
337 Err(error) => {
338 self.with_metrics(|| record_exec_error_for_path(ExecKind::Save, E::PATH, &error));
339
340 return Err(error);
341 }
342 };
343 let value = self.with_metrics(|| {
344 op(self.save_executor::<E>(contract, schema_info, schema_fingerprint))
345 })?;
346
347 Ok(map(value))
348 }
349
350 fn execute_save_with_checked_accepted_row_contract<E, T, R>(
355 &self,
356 accepted_row_decode_contract: AcceptedRowDecodeContract,
357 accepted_schema_info: SchemaInfo,
358 accepted_schema_fingerprint: CommitSchemaFingerprint,
359 op: impl FnOnce(SaveExecutor<E>) -> Result<T, InternalError>,
360 map: impl FnOnce(T) -> R,
361 ) -> Result<R, InternalError>
362 where
363 E: PersistedRow<Canister = C> + EntityValue,
364 {
365 let value = self.with_metrics(|| {
366 op(self.save_executor::<E>(
367 accepted_row_decode_contract,
368 accepted_schema_info,
369 accepted_schema_fingerprint,
370 ))
371 })?;
372
373 Ok(map(value))
374 }
375
376 fn execute_save_entity<E>(
378 &self,
379 op: impl FnOnce(SaveExecutor<E>) -> Result<E, InternalError>,
380 ) -> Result<E, InternalError>
381 where
382 E: PersistedRow<Canister = C> + EntityValue,
383 {
384 self.execute_save_with(op, std::convert::identity)
385 }
386
387 fn execute_save_batch<E>(
388 &self,
389 op: impl FnOnce(SaveExecutor<E>) -> Result<Vec<E>, InternalError>,
390 ) -> Result<WriteBatchResponse<E>, InternalError>
391 where
392 E: PersistedRow<Canister = C> + EntityValue,
393 {
394 self.execute_save_with(op, WriteBatchResponse::new)
395 }
396
397 #[must_use]
403 pub const fn load<E>(&self) -> FluentLoadQuery<'_, E>
404 where
405 E: EntityKind<Canister = C>,
406 {
407 self.fluent_load_query(MissingRowPolicy::Ignore)
408 }
409
410 #[must_use]
412 pub const fn load_with_consistency<E>(
413 &self,
414 consistency: MissingRowPolicy,
415 ) -> FluentLoadQuery<'_, E>
416 where
417 E: EntityKind<Canister = C>,
418 {
419 self.fluent_load_query(consistency)
420 }
421
422 #[must_use]
424 pub fn delete<E>(&self) -> FluentDeleteQuery<'_, E>
425 where
426 E: PersistedRow<Canister = C>,
427 {
428 self.fluent_delete_query(MissingRowPolicy::Ignore)
429 }
430
431 #[must_use]
433 pub fn delete_with_consistency<E>(
434 &self,
435 consistency: MissingRowPolicy,
436 ) -> FluentDeleteQuery<'_, E>
437 where
438 E: PersistedRow<Canister = C>,
439 {
440 self.fluent_delete_query(consistency)
441 }
442
443 #[must_use]
447 pub const fn select_one(&self) -> Value {
448 Value::Int64(1)
449 }
450
451 #[must_use]
458 pub fn show_indexes<E>(&self) -> Vec<String>
459 where
460 E: EntityKind<Canister = C>,
461 {
462 self.show_indexes_for_store_model(E::Store::PATH, E::MODEL)
463 }
464
465 #[must_use]
471 pub fn show_indexes_for_model(&self, model: &'static EntityModel) -> Vec<String> {
472 show_indexes_for_model(model)
473 }
474
475 pub fn try_show_indexes<E>(&self) -> Result<Vec<String>, InternalError>
480 where
481 E: EntityKind<Canister = C>,
482 {
483 let schema = self.accepted_schema_info_for_entity::<E>()?;
484
485 Ok(self.show_indexes_for_store_schema_info(E::Store::PATH, &schema))
486 }
487
488 pub(in crate::db) fn show_indexes_for_store_model(
492 &self,
493 store_path: &str,
494 model: &'static EntityModel,
495 ) -> Vec<String> {
496 let runtime_state = self
497 .db
498 .with_store_registry(|registry| registry.try_get_store(store_path).ok())
499 .map(|store| store.index_state());
500
501 show_indexes_for_model_with_runtime_state(model, runtime_state)
502 }
503
504 pub(in crate::db) fn show_indexes_for_store_schema_info(
508 &self,
509 store_path: &str,
510 schema: &SchemaInfo,
511 ) -> Vec<String> {
512 let runtime_state = self
513 .db
514 .with_store_registry(|registry| registry.try_get_store(store_path).ok())
515 .map(|store| store.index_state());
516
517 show_indexes_for_schema_info_with_runtime_state(schema, runtime_state)
518 }
519
520 #[must_use]
526 pub fn show_columns<E>(&self) -> Vec<EntityFieldDescription>
527 where
528 E: EntityKind<Canister = C>,
529 {
530 self.show_columns_for_model(E::MODEL)
531 }
532
533 #[must_use]
535 pub fn show_columns_for_model(
536 &self,
537 model: &'static EntityModel,
538 ) -> Vec<EntityFieldDescription> {
539 describe_entity_fields(model)
540 }
541
542 pub fn try_show_columns<E>(&self) -> Result<Vec<EntityFieldDescription>, InternalError>
548 where
549 E: EntityKind<Canister = C>,
550 {
551 let snapshot = self.ensure_accepted_schema_snapshot::<E>()?;
552
553 Ok(describe_entity_fields_with_persisted_schema(&snapshot))
554 }
555
556 #[must_use]
558 pub fn show_entities(&self) -> Vec<crate::db::EntityCatalogDescription> {
559 self.try_show_entities().expect("session invariant")
560 }
561
562 pub fn try_show_entities(
564 &self,
565 ) -> Result<Vec<crate::db::EntityCatalogDescription>, InternalError> {
566 self.db.runtime_entity_catalog()
567 }
568
569 #[must_use]
571 pub fn show_stores(&self) -> Vec<StoreCatalogDescription> {
572 self.db.runtime_store_catalog()
573 }
574
575 #[must_use]
577 pub fn show_memory(&self) -> Vec<crate::db::MemoryCatalogDescription> {
578 self.db.runtime_memory_catalog()
579 }
580
581 #[cfg(feature = "sql")]
584 fn visible_indexes_for_store_accepted_schema(
585 &self,
586 store_path: &str,
587 schema_info: &SchemaInfo,
588 ) -> Result<VisibleIndexes<'static>, QueryError> {
589 let store = self
592 .db
593 .recovered_store(store_path)
594 .map_err(QueryError::execute)?;
595 let state = store.index_state();
596 if state != IndexState::Ready {
597 return Ok(VisibleIndexes::none());
598 }
599 debug_assert_eq!(state, IndexState::Ready);
600
601 let visible_indexes = VisibleIndexes::accepted_schema_visible(schema_info);
604 debug_assert!(visible_indexes.accepted_field_path_contracts_are_consistent());
605 debug_assert!(visible_indexes.accepted_expression_contracts_are_consistent());
606 debug_assert_eq!(
607 visible_indexes.accepted_expression_index_count(),
608 Some(visible_indexes.accepted_expression_indexes().len()),
609 );
610
611 Ok(visible_indexes)
612 }
613
614 #[must_use]
620 pub fn describe_entity<E>(&self) -> EntitySchemaDescription
621 where
622 E: EntityKind<Canister = C>,
623 {
624 self.describe_entity_model(E::MODEL)
625 }
626
627 #[must_use]
629 pub fn describe_entity_model(&self, model: &'static EntityModel) -> EntitySchemaDescription {
630 describe_entity_model(model)
631 }
632
633 pub fn try_describe_entity<E>(&self) -> Result<EntitySchemaDescription, InternalError>
639 where
640 E: EntityKind<Canister = C>,
641 {
642 let snapshot = self.ensure_accepted_schema_snapshot::<E>()?;
643
644 Ok(describe_entity_model_with_persisted_schema(
645 E::MODEL,
646 &snapshot,
647 ))
648 }
649
650 fn ensure_accepted_schema_snapshot<E>(&self) -> Result<AcceptedSchemaSnapshot, InternalError>
654 where
655 E: EntityKind<Canister = C>,
656 {
657 let store = self.db.recovered_store(E::Store::PATH)?;
658
659 store.with_schema_mut(|schema_store| {
660 ensure_accepted_schema_snapshot(schema_store, E::ENTITY_TAG, E::PATH, E::MODEL)
661 })
662 }
663
664 pub(in crate::db::session) fn accepted_schema_catalog_context_for_query<E>(
669 &self,
670 ) -> Result<AcceptedSchemaCatalogContext, InternalError>
671 where
672 E: EntityKind<Canister = C>,
673 {
674 let cache_key = (self.db.cache_scope_id(), E::PATH);
675 if let Some(entry) =
676 ACCEPTED_SCHEMA_QUERY_CACHES.with(|cache| cache.borrow().get(&cache_key).cloned())
677 {
678 return Ok(AcceptedSchemaCatalogContext::new(
679 entry.snapshot,
680 entry.identity,
681 ));
682 }
683
684 let snapshot = self.load_accepted_schema_snapshot_for_query::<E>()?;
685 let fingerprint = accepted_schema_cache_fingerprint(&snapshot)?;
686 let identity = AcceptedCatalogIdentity::new(
687 E::ENTITY_TAG,
688 E::PATH,
689 E::Store::PATH,
690 snapshot.persisted_snapshot().version(),
691 fingerprint,
692 );
693 ACCEPTED_SCHEMA_QUERY_CACHES.with(|cache| {
694 cache.borrow_mut().insert(
695 cache_key,
696 AcceptedSchemaQueryCacheEntry {
697 snapshot: snapshot.clone(),
698 identity,
699 },
700 );
701 });
702
703 Ok(AcceptedSchemaCatalogContext::new(snapshot, identity))
704 }
705
706 pub(in crate::db::session) fn accepted_catalog_snapshot_selection_for_query<E>(
707 &self,
708 ) -> Result<Option<AcceptedCatalogSnapshotSelection>, InternalError>
709 where
710 E: EntityKind<Canister = C>,
711 {
712 let store = self.db.recovered_store(E::Store::PATH)?;
713
714 store.with_schema_mut(|schema_store| {
715 schema_store.latest_catalog_identity(E::ENTITY_TAG, E::PATH, E::Store::PATH)
716 })
717 }
718
719 pub(in crate::db::session) fn accepted_schema_catalog_context_from_selection<E>(
720 &self,
721 selection: &AcceptedCatalogSnapshotSelection,
722 ) -> Result<Option<AcceptedSchemaCatalogContext>, InternalError>
723 where
724 E: EntityKind<Canister = C>,
725 {
726 let snapshot = selection.decode_verified()?;
727 if snapshot.persisted_snapshot().fields().len() != E::MODEL.fields().len() {
728 return Ok(None);
729 }
730 let context = AcceptedSchemaCatalogContext::new(snapshot.clone(), selection.identity());
731 let cache_key = (self.db.cache_scope_id(), E::PATH);
732
733 ACCEPTED_SCHEMA_QUERY_CACHES.with(|cache| {
734 cache.borrow_mut().insert(
735 cache_key,
736 AcceptedSchemaQueryCacheEntry {
737 snapshot,
738 identity: selection.identity(),
739 },
740 );
741 });
742
743 Ok(Some(context))
744 }
745
746 #[cfg(feature = "sql")]
747 fn invalidate_accepted_schema_query_cache_for_entity<E>(&self)
748 where
749 E: EntityKind<Canister = C>,
750 {
751 let cache_key = (self.db.cache_scope_id(), E::PATH);
752 ACCEPTED_SCHEMA_QUERY_CACHES.with(|cache| {
753 cache.borrow_mut().remove(&cache_key);
754 });
755 }
756
757 fn load_accepted_schema_snapshot_for_query<E>(
758 &self,
759 ) -> Result<AcceptedSchemaSnapshot, InternalError>
760 where
761 E: EntityKind<Canister = C>,
762 {
763 let store = self.db.recovered_store(E::Store::PATH)?;
764
765 store.with_schema_mut(|schema_store| {
766 if let Some(snapshot) = schema_store.latest_persisted_snapshot(E::ENTITY_TAG)? {
767 let accepted = AcceptedSchemaSnapshot::try_new(snapshot)?;
768 if AcceptedRowLayoutRuntimeContract::from_generated_compatible_schema(
769 &accepted,
770 E::MODEL,
771 )
772 .is_ok()
773 {
774 return Ok(accepted);
775 }
776 }
777
778 ensure_accepted_schema_snapshot(schema_store, E::ENTITY_TAG, E::PATH, E::MODEL)
779 })
780 }
781
782 pub(in crate::db) fn accepted_schema_info_for_entity<E>(
786 &self,
787 ) -> Result<SchemaInfo, InternalError>
788 where
789 E: EntityKind<Canister = C>,
790 {
791 let catalog = self.accepted_schema_catalog_context_for_query::<E>()?;
792
793 Ok(catalog.accepted_schema_info_for::<E>())
794 }
795
796 #[cfg(feature = "sql")]
800 pub(in crate::db) fn accepted_entity_authority_for_schema<E>(
801 accepted_schema: &AcceptedSchemaSnapshot,
802 ) -> Result<EntityAuthority, InternalError>
803 where
804 E: EntityKind<Canister = C>,
805 {
806 EntityAuthority::from_accepted_schema_for_type::<E>(accepted_schema)
807 }
808
809 fn ensure_generated_compatible_accepted_save_schema<E>(
814 &self,
815 ) -> Result<
816 (
817 AcceptedRowDecodeContract,
818 SchemaInfo,
819 CommitSchemaFingerprint,
820 ),
821 InternalError,
822 >
823 where
824 E: EntityKind<Canister = C>,
825 {
826 let accepted_schema = self.ensure_accepted_schema_snapshot::<E>()?;
827 let (accepted_row_layout, _) =
828 AcceptedRowLayoutRuntimeContract::from_generated_compatible_schema(
829 &accepted_schema,
830 E::MODEL,
831 )?;
832 let (row_decode_contract, _, schema_info, schema_fingerprint) =
833 accepted_save_contract_for_descriptor::<E>(&accepted_schema, &accepted_row_layout)?;
834
835 Ok((row_decode_contract, schema_info, schema_fingerprint))
836 }
837
838 pub fn storage_report(
840 &self,
841 name_to_path: &[(&'static str, &'static str)],
842 ) -> Result<StorageReport, InternalError> {
843 self.db.storage_report(name_to_path)
844 }
845
846 pub fn storage_report_default(&self) -> Result<StorageReport, InternalError> {
848 self.db.storage_report_default()
849 }
850
851 pub fn integrity_report(&self) -> Result<IntegrityReport, InternalError> {
853 self.db.integrity_report()
854 }
855
856 #[must_use]
861 pub(in crate::db) const fn load_executor<E>(&self) -> LoadExecutor<E>
862 where
863 E: EntityKind<Canister = C> + EntityValue,
864 {
865 LoadExecutor::new(self.db, self.debug)
866 }
867
868 #[must_use]
869 pub(in crate::db) const fn delete_executor<E>(&self) -> DeleteExecutor<E>
870 where
871 E: PersistedRow<Canister = C> + EntityValue,
872 {
873 DeleteExecutor::new(self.db)
874 }
875
876 #[must_use]
877 pub(in crate::db) const fn save_executor<E>(
878 &self,
879 accepted_row_decode_contract: AcceptedRowDecodeContract,
880 accepted_schema_info: SchemaInfo,
881 accepted_schema_fingerprint: CommitSchemaFingerprint,
882 ) -> SaveExecutor<E>
883 where
884 E: PersistedRow<Canister = C> + EntityValue,
885 {
886 SaveExecutor::new_with_accepted_contract(
887 self.db,
888 self.debug,
889 accepted_row_decode_contract,
890 accepted_schema_info,
891 accepted_schema_fingerprint,
892 )
893 }
894}