oxgraph_db/catalog.rs
1//! Catalog metadata for names, projections, property keys, and indexes.
2
3use std::collections::{BTreeMap, BTreeSet};
4
5use serde::{Deserialize, Serialize};
6
7use crate::{
8 DbError, IndexId, LabelId, ProjectionId, PropertyKeyId, RelationTypeId, RoleId,
9 value::PropertyType,
10};
11
12/// Catalog entry for one structural incidence role.
13///
14/// # Performance
15///
16/// Cloning is `O(name length)`.
17#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
18pub struct RoleDefinition {
19 /// Stable role identifier.
20 pub id: RoleId,
21 /// Human-readable unique role name.
22 pub name: String,
23}
24
25/// Catalog entry for one element or relation label.
26///
27/// # Performance
28///
29/// Cloning is `O(name length)`.
30#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
31pub struct LabelDefinition {
32 /// Stable label identifier.
33 pub id: LabelId,
34 /// Human-readable unique label name.
35 pub name: String,
36}
37
38/// Catalog entry for one relation type.
39///
40/// # Performance
41///
42/// Cloning is `O(name length)`.
43#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
44pub struct RelationTypeDefinition {
45 /// Stable relation type identifier.
46 pub id: RelationTypeId,
47 /// Human-readable unique relation type name.
48 pub name: String,
49}
50
51/// Subject family accepted by a property key.
52///
53/// # Performance
54///
55/// Copying and comparing are `O(1)`.
56#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
57pub enum PropertyFamily {
58 /// Property applies to canonical elements.
59 Element,
60 /// Property applies to canonical relations.
61 Relation,
62 /// Property applies to canonical incidences.
63 Incidence,
64}
65
66/// Catalog entry for one typed property key.
67///
68/// # Performance
69///
70/// Cloning is `O(name length)`.
71#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
72pub struct PropertyKeyDefinition {
73 /// Stable property key identifier.
74 pub id: PropertyKeyId,
75 /// Human-readable unique key name.
76 pub name: String,
77 /// Subject family this key can be attached to.
78 pub family: PropertyFamily,
79 /// Required scalar value type.
80 pub value_type: PropertyType,
81}
82
83/// Graph projection definition.
84///
85/// Graph projections materialize binary relations as CSR outgoing and CSC
86/// incoming arrays over canonical topology IDs.
87///
88/// # Performance
89///
90/// Cloning is `O(r)` for `r` selected relation types plus the name length.
91#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
92pub struct GraphProjectionDefinition {
93 /// Unique projection name.
94 pub name: String,
95 /// Relation types visible as binary graph edges.
96 pub relation_types: BTreeSet<RelationTypeId>,
97 /// Role identifying the source endpoint.
98 pub source_role: RoleId,
99 /// Role identifying the target endpoint.
100 pub target_role: RoleId,
101}
102
103/// Hypergraph projection definition.
104///
105/// Hypergraph projections materialize many-participant directed relations as
106/// BCSR-style relation-major and vertex-major arrays.
107///
108/// # Performance
109///
110/// Cloning is `O(r + s + t)` for relation type and role set sizes plus the
111/// name length.
112#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
113pub struct HypergraphProjectionDefinition {
114 /// Unique projection name.
115 pub name: String,
116 /// Relation types visible as hyperedges.
117 pub relation_types: BTreeSet<RelationTypeId>,
118 /// Roles treated as source-side participants.
119 pub source_roles: BTreeSet<RoleId>,
120 /// Roles treated as target-side participants.
121 pub target_roles: BTreeSet<RoleId>,
122}
123
124/// Physical projection definition stored in the catalog.
125///
126/// # Performance
127///
128/// Cloning is `O(definition size)`.
129#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
130pub enum ProjectionDefinition {
131 /// Binary graph projection.
132 Graph(GraphProjectionDefinition),
133 /// Directed hypergraph projection.
134 Hypergraph(HypergraphProjectionDefinition),
135}
136
137impl ProjectionDefinition {
138 /// Returns the unique projection name.
139 ///
140 /// # Performance
141 ///
142 /// This method is `O(1)`.
143 #[must_use]
144 pub fn name(&self) -> &str {
145 match self {
146 Self::Graph(definition) => &definition.name,
147 Self::Hypergraph(definition) => &definition.name,
148 }
149 }
150}
151
152/// Index definition stored in the catalog.
153///
154/// # Performance
155///
156/// Cloning is `O(key count)` for composite indexes and `O(1)` otherwise.
157#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
158pub enum IndexDefinition {
159 /// Element label membership index.
160 Label {
161 /// Indexed label.
162 label: LabelId,
163 },
164 /// Relation type membership index.
165 RelationType {
166 /// Indexed relation type.
167 relation_type: RelationTypeId,
168 },
169 /// Equality index over one property key.
170 PropertyEquality {
171 /// Indexed property key.
172 key: PropertyKeyId,
173 },
174 /// Range index over one ordered property key.
175 PropertyRange {
176 /// Indexed property key.
177 key: PropertyKeyId,
178 },
179 /// Composite equality index over ordered property keys.
180 CompositeEquality {
181 /// Indexed property keys in tuple order.
182 keys: Vec<PropertyKeyId>,
183 },
184 /// Projection-materialization index metadata.
185 Projection {
186 /// Indexed projection.
187 projection: ProjectionId,
188 },
189}
190
191/// Catalog entry for one index.
192///
193/// # Performance
194///
195/// Cloning is `O(name length + definition size)`.
196#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
197pub struct IndexEntry {
198 /// Stable index identifier.
199 pub id: IndexId,
200 /// Human-readable unique index name.
201 pub name: String,
202 /// Logical index definition.
203 pub definition: IndexDefinition,
204}
205
206/// Catalog entry for one projection.
207///
208/// # Performance
209///
210/// Cloning is `O(name length + definition size)`.
211#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
212pub struct ProjectionEntry {
213 /// Stable projection identifier.
214 pub id: ProjectionId,
215 /// Physical projection definition.
216 pub definition: ProjectionDefinition,
217}
218
219/// Db catalog for names, schemas, projections, and indexes.
220///
221/// # Performance
222///
223/// Cloning is `O(catalog entries + string bytes)`.
224#[derive(Clone, Debug, Eq, PartialEq)]
225pub struct Catalog {
226 /// Roles by stable ID.
227 roles: BTreeMap<RoleId, RoleDefinition>,
228 /// Role IDs by name.
229 role_names: BTreeMap<String, RoleId>,
230 /// Labels by stable ID.
231 labels: BTreeMap<LabelId, LabelDefinition>,
232 /// Label IDs by name.
233 label_names: BTreeMap<String, LabelId>,
234 /// Relation types by stable ID.
235 relation_types: BTreeMap<RelationTypeId, RelationTypeDefinition>,
236 /// Relation type IDs by name.
237 relation_type_names: BTreeMap<String, RelationTypeId>,
238 /// Property keys by stable ID.
239 property_keys: BTreeMap<PropertyKeyId, PropertyKeyDefinition>,
240 /// Property key IDs by name.
241 property_key_names: BTreeMap<String, PropertyKeyId>,
242 /// Projections by stable ID.
243 projections: BTreeMap<ProjectionId, ProjectionEntry>,
244 /// Projection IDs by name.
245 projection_names: BTreeMap<String, ProjectionId>,
246 /// Indexes by stable ID.
247 indexes: BTreeMap<IndexId, IndexEntry>,
248 /// Index IDs by name.
249 index_names: BTreeMap<String, IndexId>,
250}
251
252impl Catalog {
253 /// Creates an empty catalog.
254 ///
255 /// # Performance
256 ///
257 /// This function is `O(1)`.
258 #[must_use]
259 pub(crate) const fn empty() -> Self {
260 Self {
261 roles: BTreeMap::new(),
262 role_names: BTreeMap::new(),
263 labels: BTreeMap::new(),
264 label_names: BTreeMap::new(),
265 relation_types: BTreeMap::new(),
266 relation_type_names: BTreeMap::new(),
267 property_keys: BTreeMap::new(),
268 property_key_names: BTreeMap::new(),
269 projections: BTreeMap::new(),
270 projection_names: BTreeMap::new(),
271 indexes: BTreeMap::new(),
272 index_names: BTreeMap::new(),
273 }
274 }
275
276 /// Returns a role definition.
277 ///
278 /// # Performance
279 ///
280 /// This method is `O(log r)`.
281 #[must_use]
282 pub fn role(&self, id: RoleId) -> Option<&RoleDefinition> {
283 self.roles.get(&id)
284 }
285
286 /// Returns a label definition.
287 ///
288 /// # Performance
289 ///
290 /// This method is `O(log l)`.
291 #[must_use]
292 pub fn label(&self, id: LabelId) -> Option<&LabelDefinition> {
293 self.labels.get(&id)
294 }
295
296 /// Returns a relation type definition.
297 ///
298 /// # Performance
299 ///
300 /// This method is `O(log t)`.
301 #[must_use]
302 pub fn relation_type(&self, id: RelationTypeId) -> Option<&RelationTypeDefinition> {
303 self.relation_types.get(&id)
304 }
305
306 /// Returns a property key definition.
307 ///
308 /// # Performance
309 ///
310 /// This method is `O(log p)`.
311 #[must_use]
312 pub fn property_key(&self, id: PropertyKeyId) -> Option<&PropertyKeyDefinition> {
313 self.property_keys.get(&id)
314 }
315
316 /// Returns a projection entry.
317 ///
318 /// # Performance
319 ///
320 /// This method is `O(log p)`.
321 #[must_use]
322 pub fn projection(&self, id: ProjectionId) -> Option<&ProjectionEntry> {
323 self.projections.get(&id)
324 }
325
326 /// Returns an index entry.
327 ///
328 /// # Performance
329 ///
330 /// This method is `O(log i)`.
331 #[must_use]
332 pub fn index(&self, id: IndexId) -> Option<&IndexEntry> {
333 self.indexes.get(&id)
334 }
335
336 /// Resolves a role name.
337 ///
338 /// # Performance
339 ///
340 /// This method is `O(log r + name length)`.
341 #[must_use]
342 pub fn role_id(&self, name: &str) -> Option<RoleId> {
343 self.role_names.get(name).copied()
344 }
345
346 /// Resolves a label name.
347 ///
348 /// # Performance
349 ///
350 /// This method is `O(log l + name length)`.
351 #[must_use]
352 pub fn label_id(&self, name: &str) -> Option<LabelId> {
353 self.label_names.get(name).copied()
354 }
355
356 /// Resolves a relation type name.
357 ///
358 /// # Performance
359 ///
360 /// This method is `O(log t + name length)`.
361 #[must_use]
362 pub fn relation_type_id(&self, name: &str) -> Option<RelationTypeId> {
363 self.relation_type_names.get(name).copied()
364 }
365
366 /// Resolves a property key name.
367 ///
368 /// # Performance
369 ///
370 /// This method is `O(log p + name length)`.
371 #[must_use]
372 pub fn property_key_id(&self, name: &str) -> Option<PropertyKeyId> {
373 self.property_key_names.get(name).copied()
374 }
375
376 /// Resolves a projection name.
377 ///
378 /// # Performance
379 ///
380 /// This method is `O(log p + name length)`.
381 #[must_use]
382 pub fn projection_id(&self, name: &str) -> Option<ProjectionId> {
383 self.projection_names.get(name).copied()
384 }
385
386 /// Resolves an index name.
387 ///
388 /// # Performance
389 ///
390 /// This method is `O(log i + name length)`.
391 #[must_use]
392 pub fn index_id(&self, name: &str) -> Option<IndexId> {
393 self.index_names.get(name).copied()
394 }
395
396 /// Iterates role definitions in ID order.
397 ///
398 /// # Performance
399 ///
400 /// Creating the iterator is `O(1)`.
401 pub fn roles(&self) -> impl Iterator<Item = &RoleDefinition> {
402 self.roles.values()
403 }
404
405 /// Iterates label definitions in ID order.
406 ///
407 /// # Performance
408 ///
409 /// Creating the iterator is `O(1)`.
410 pub fn labels(&self) -> impl Iterator<Item = &LabelDefinition> {
411 self.labels.values()
412 }
413
414 /// Iterates relation type definitions in ID order.
415 ///
416 /// # Performance
417 ///
418 /// Creating the iterator is `O(1)`.
419 pub fn relation_types(&self) -> impl Iterator<Item = &RelationTypeDefinition> {
420 self.relation_types.values()
421 }
422
423 /// Iterates property key definitions in ID order.
424 ///
425 /// # Performance
426 ///
427 /// Creating the iterator is `O(1)`.
428 pub fn property_keys(&self) -> impl Iterator<Item = &PropertyKeyDefinition> {
429 self.property_keys.values()
430 }
431
432 /// Iterates projection entries in ID order.
433 ///
434 /// # Performance
435 ///
436 /// Creating the iterator is `O(1)`.
437 pub fn projections(&self) -> impl Iterator<Item = &ProjectionEntry> {
438 self.projections.values()
439 }
440
441 /// Iterates index entries in ID order.
442 ///
443 /// # Performance
444 ///
445 /// Creating the iterator is `O(1)`.
446 pub fn indexes(&self) -> impl Iterator<Item = &IndexEntry> {
447 self.indexes.values()
448 }
449
450 /// Registers a structural role.
451 pub(crate) fn insert_role(&mut self, id: RoleId, name: String) -> Result<(), DbError> {
452 insert_named(&mut self.role_names, &name, id)?;
453 self.roles.insert(id, RoleDefinition { id, name });
454 Ok(())
455 }
456
457 /// Registers a label.
458 pub(crate) fn insert_label(&mut self, id: LabelId, name: String) -> Result<(), DbError> {
459 insert_named(&mut self.label_names, &name, id)?;
460 self.labels.insert(id, LabelDefinition { id, name });
461 Ok(())
462 }
463
464 /// Registers a relation type.
465 pub(crate) fn insert_relation_type(
466 &mut self,
467 id: RelationTypeId,
468 name: String,
469 ) -> Result<(), DbError> {
470 insert_named(&mut self.relation_type_names, &name, id)?;
471 self.relation_types
472 .insert(id, RelationTypeDefinition { id, name });
473 Ok(())
474 }
475
476 /// Registers a typed property key.
477 pub(crate) fn insert_property_key(
478 &mut self,
479 definition: PropertyKeyDefinition,
480 ) -> Result<(), DbError> {
481 insert_named(
482 &mut self.property_key_names,
483 &definition.name,
484 definition.id,
485 )?;
486 self.property_keys.insert(definition.id, definition);
487 Ok(())
488 }
489
490 /// Registers a projection definition.
491 pub(crate) fn insert_projection(
492 &mut self,
493 id: ProjectionId,
494 definition: ProjectionDefinition,
495 ) -> Result<(), DbError> {
496 insert_named(&mut self.projection_names, definition.name(), id)?;
497 self.projections
498 .insert(id, ProjectionEntry { id, definition });
499 Ok(())
500 }
501
502 /// Registers an index definition.
503 pub(crate) fn insert_index(
504 &mut self,
505 id: IndexId,
506 name: String,
507 definition: IndexDefinition,
508 ) -> Result<(), DbError> {
509 insert_named(&mut self.index_names, &name, id)?;
510 self.indexes.insert(
511 id,
512 IndexEntry {
513 id,
514 name,
515 definition,
516 },
517 );
518 Ok(())
519 }
520}
521
522/// Inserts one unique name into a catalog name map.
523fn insert_named<Id: Copy>(
524 names: &mut BTreeMap<String, Id>,
525 name: &str,
526 id: Id,
527) -> Result<(), DbError> {
528 if names.contains_key(name) {
529 return Err(DbError::DuplicateCatalogName);
530 }
531 names.insert(name.to_owned(), id);
532 Ok(())
533}