icydb_core/db/session/
catalog.rs1#[cfg(any(test, feature = "sql-explain"))]
10use crate::db::{IndexState, QueryError, query::plan::VisibleIndexes};
11use crate::{
12 db::{
13 DbSession, EntityCatalogCounts, EntityCatalogDescription, EntityFieldDescription,
14 EntitySchemaDescription, IntegrityReport, StorageReport, StoreCatalogDescription,
15 schema::{
16 PersistedFieldKind, PersistedFieldSnapshot, SchemaInfo, describe_entity_fields,
17 describe_entity_fields_with_persisted_schema, describe_entity_model,
18 describe_entity_model_with_persisted_schema, show_indexes_for_model,
19 show_indexes_for_model_with_runtime_state,
20 show_indexes_for_schema_info_with_runtime_state,
21 },
22 },
23 error::InternalError,
24 model::entity::EntityModel,
25 traits::{CanisterKind, EntityKind, Path},
26};
27
28fn relation_field_count(fields: &[PersistedFieldSnapshot]) -> usize {
29 fields
30 .iter()
31 .filter(|field| persisted_kind_is_relation_field(field.kind()))
32 .count()
33}
34
35fn persisted_kind_is_relation_field(kind: &PersistedFieldKind) -> bool {
36 match kind {
37 PersistedFieldKind::Relation { .. } => true,
38 PersistedFieldKind::List(inner) | PersistedFieldKind::Set(inner) => {
39 matches!(inner.as_ref(), PersistedFieldKind::Relation { .. })
40 }
41 _ => false,
42 }
43}
44
45impl<C: CanisterKind> DbSession<C> {
46 #[must_use]
53 pub fn show_indexes<E>(&self) -> Vec<String>
54 where
55 E: EntityKind<Canister = C>,
56 {
57 self.show_indexes_for_store_model(E::Store::PATH, E::MODEL)
58 }
59
60 #[must_use]
66 pub fn show_indexes_for_model(&self, model: &'static EntityModel) -> Vec<String> {
67 show_indexes_for_model(model)
68 }
69
70 pub fn try_show_indexes<E>(&self) -> Result<Vec<String>, InternalError>
75 where
76 E: EntityKind<Canister = C>,
77 {
78 let schema = self.accepted_schema_info_for_entity::<E>()?;
79
80 Ok(self.show_indexes_for_store_schema_info(E::Store::PATH, &schema))
81 }
82
83 pub(in crate::db) fn show_indexes_for_store_model(
87 &self,
88 store_path: &str,
89 model: &'static EntityModel,
90 ) -> Vec<String> {
91 let runtime_state = self
92 .db
93 .with_store_registry(|registry| registry.try_get_store(store_path).ok())
94 .map(|store| store.index_state());
95
96 show_indexes_for_model_with_runtime_state(model, runtime_state)
97 }
98
99 pub(in crate::db) fn show_indexes_for_store_schema_info(
103 &self,
104 store_path: &str,
105 schema: &SchemaInfo,
106 ) -> Vec<String> {
107 let runtime_state = self
108 .db
109 .with_store_registry(|registry| registry.try_get_store(store_path).ok())
110 .map(|store| store.index_state());
111
112 show_indexes_for_schema_info_with_runtime_state(schema, runtime_state)
113 }
114
115 #[must_use]
121 pub fn show_columns<E>(&self) -> Vec<EntityFieldDescription>
122 where
123 E: EntityKind<Canister = C>,
124 {
125 self.show_columns_for_model(E::MODEL)
126 }
127
128 #[must_use]
130 pub fn show_columns_for_model(
131 &self,
132 model: &'static EntityModel,
133 ) -> Vec<EntityFieldDescription> {
134 describe_entity_fields(model)
135 }
136
137 pub fn try_show_columns<E>(&self) -> Result<Vec<EntityFieldDescription>, InternalError>
143 where
144 E: EntityKind<Canister = C>,
145 {
146 let snapshot = self.ensure_accepted_schema_snapshot::<E>()?;
147
148 Ok(describe_entity_fields_with_persisted_schema(&snapshot))
149 }
150
151 #[must_use]
158 pub fn show_entities(&self) -> Vec<crate::db::EntityCatalogDescription> {
159 self.try_show_entities().expect("session invariant")
160 }
161
162 pub fn try_show_entities(&self) -> Result<Vec<EntityCatalogDescription>, InternalError> {
164 let mut entities = Vec::with_capacity(self.db.entity_runtime_hooks.len());
165
166 for hooks in self.db.entity_runtime_hooks {
167 let store = self.db.recovered_store(hooks.store_path)?;
168 let storage = store
169 .storage_capabilities()
170 .storage_mode()
171 .as_str()
172 .to_string();
173 let accepted = self.accepted_schema_catalog_context_for_runtime_hook(hooks, store)?;
174 let snapshot = accepted.snapshot().persisted_snapshot();
175
176 entities.push(EntityCatalogDescription::new(
177 snapshot.entity_name().to_string(),
178 snapshot.entity_path().to_string(),
179 hooks.store_path.to_string(),
180 storage,
181 EntityCatalogCounts::new(
182 u32::try_from(snapshot.fields().len()).unwrap_or(u32::MAX),
183 u32::try_from(snapshot.indexes().len()).unwrap_or(u32::MAX),
184 u32::try_from(relation_field_count(snapshot.fields())).unwrap_or(u32::MAX),
185 snapshot.version().get(),
186 ),
187 ));
188 }
189
190 Ok(entities)
191 }
192
193 #[must_use]
195 pub fn show_stores(&self) -> Vec<StoreCatalogDescription> {
196 self.db.runtime_store_catalog()
197 }
198
199 #[must_use]
201 pub fn show_memory(&self) -> Vec<crate::db::MemoryCatalogDescription> {
202 self.db.runtime_memory_catalog()
203 }
204
205 #[cfg(any(test, feature = "sql-explain"))]
208 pub(in crate::db::session) fn visible_indexes_for_store_accepted_schema(
209 &self,
210 store_path: &str,
211 schema_info: &SchemaInfo,
212 ) -> Result<VisibleIndexes<'static>, QueryError> {
213 let store = self
216 .db
217 .recovered_store(store_path)
218 .map_err(QueryError::execute)?;
219 let state = store.index_state();
220 if state != IndexState::Ready {
221 return Ok(VisibleIndexes::none());
222 }
223 debug_assert_eq!(state, IndexState::Ready);
224
225 let visible_indexes = VisibleIndexes::accepted_schema_visible(schema_info);
228 debug_assert!(visible_indexes.accepted_field_path_contracts_are_consistent());
229 debug_assert!(visible_indexes.accepted_expression_contracts_are_consistent());
230 debug_assert_eq!(
231 visible_indexes.accepted_expression_index_count(),
232 Some(visible_indexes.accepted_expression_indexes().len()),
233 );
234
235 Ok(visible_indexes)
236 }
237
238 #[must_use]
244 pub fn describe_entity<E>(&self) -> EntitySchemaDescription
245 where
246 E: EntityKind<Canister = C>,
247 {
248 self.describe_entity_model(E::MODEL)
249 }
250
251 #[must_use]
253 pub fn describe_entity_model(&self, model: &'static EntityModel) -> EntitySchemaDescription {
254 describe_entity_model(model)
255 }
256
257 pub fn try_describe_entity<E>(&self) -> Result<EntitySchemaDescription, InternalError>
263 where
264 E: EntityKind<Canister = C>,
265 {
266 let snapshot = self.ensure_accepted_schema_snapshot::<E>()?;
267
268 Ok(describe_entity_model_with_persisted_schema(
269 E::MODEL,
270 &snapshot,
271 ))
272 }
273
274 pub fn storage_report(
276 &self,
277 name_to_path: &[(&'static str, &'static str)],
278 ) -> Result<StorageReport, InternalError> {
279 self.db.storage_report(name_to_path)
280 }
281
282 pub fn storage_report_default(&self) -> Result<StorageReport, InternalError> {
284 self.db.storage_report_default()
285 }
286
287 pub fn integrity_report(&self) -> Result<IntegrityReport, InternalError> {
289 self.db.integrity_report()
290 }
291}