1use std::collections::HashMap;
15use std::sync::Arc;
16use std::sync::atomic::{AtomicU32, Ordering};
17
18use parking_lot::RwLock;
19
20use grafeo_common::types::{EdgeTypeId, IndexId, LabelId, PropertyKeyId, Value};
21
22pub struct Catalog {
27 labels: LabelCatalog,
29 property_keys: PropertyCatalog,
31 edge_types: EdgeTypeCatalog,
33 indexes: IndexCatalog,
35 schema: Option<SchemaCatalog>,
37}
38
39impl Catalog {
40 #[must_use]
42 pub fn new() -> Self {
43 Self {
44 labels: LabelCatalog::new(),
45 property_keys: PropertyCatalog::new(),
46 edge_types: EdgeTypeCatalog::new(),
47 indexes: IndexCatalog::new(),
48 schema: Some(SchemaCatalog::new()),
49 }
50 }
51
52 #[must_use]
56 pub fn with_schema() -> Self {
57 Self::new()
58 }
59
60 pub fn get_or_create_label(&self, name: &str) -> LabelId {
64 self.labels.get_or_create(name)
65 }
66
67 #[must_use]
69 pub fn get_label_id(&self, name: &str) -> Option<LabelId> {
70 self.labels.get_id(name)
71 }
72
73 #[must_use]
75 pub fn get_label_name(&self, id: LabelId) -> Option<Arc<str>> {
76 self.labels.get_name(id)
77 }
78
79 #[must_use]
81 pub fn label_count(&self) -> usize {
82 self.labels.count()
83 }
84
85 #[must_use]
87 pub fn all_labels(&self) -> Vec<Arc<str>> {
88 self.labels.all_names()
89 }
90
91 pub fn get_or_create_property_key(&self, name: &str) -> PropertyKeyId {
95 self.property_keys.get_or_create(name)
96 }
97
98 #[must_use]
100 pub fn get_property_key_id(&self, name: &str) -> Option<PropertyKeyId> {
101 self.property_keys.get_id(name)
102 }
103
104 #[must_use]
106 pub fn get_property_key_name(&self, id: PropertyKeyId) -> Option<Arc<str>> {
107 self.property_keys.get_name(id)
108 }
109
110 #[must_use]
112 pub fn property_key_count(&self) -> usize {
113 self.property_keys.count()
114 }
115
116 #[must_use]
118 pub fn all_property_keys(&self) -> Vec<Arc<str>> {
119 self.property_keys.all_names()
120 }
121
122 pub fn get_or_create_edge_type(&self, name: &str) -> EdgeTypeId {
126 self.edge_types.get_or_create(name)
127 }
128
129 #[must_use]
131 pub fn get_edge_type_id(&self, name: &str) -> Option<EdgeTypeId> {
132 self.edge_types.get_id(name)
133 }
134
135 #[must_use]
137 pub fn get_edge_type_name(&self, id: EdgeTypeId) -> Option<Arc<str>> {
138 self.edge_types.get_name(id)
139 }
140
141 #[must_use]
143 pub fn edge_type_count(&self) -> usize {
144 self.edge_types.count()
145 }
146
147 #[must_use]
149 pub fn all_edge_types(&self) -> Vec<Arc<str>> {
150 self.edge_types.all_names()
151 }
152
153 pub fn create_index(
157 &self,
158 label: LabelId,
159 property_key: PropertyKeyId,
160 index_type: IndexType,
161 ) -> IndexId {
162 self.indexes.create(label, property_key, index_type)
163 }
164
165 pub fn drop_index(&self, id: IndexId) -> bool {
167 self.indexes.drop(id)
168 }
169
170 #[must_use]
172 pub fn get_index(&self, id: IndexId) -> Option<IndexDefinition> {
173 self.indexes.get(id)
174 }
175
176 #[must_use]
178 pub fn indexes_for_label(&self, label: LabelId) -> Vec<IndexId> {
179 self.indexes.for_label(label)
180 }
181
182 #[must_use]
184 pub fn indexes_for_label_property(
185 &self,
186 label: LabelId,
187 property_key: PropertyKeyId,
188 ) -> Vec<IndexId> {
189 self.indexes.for_label_property(label, property_key)
190 }
191
192 #[must_use]
194 pub fn index_count(&self) -> usize {
195 self.indexes.count()
196 }
197
198 #[must_use]
202 pub fn has_schema(&self) -> bool {
203 self.schema.is_some()
204 }
205
206 pub fn add_unique_constraint(
210 &self,
211 label: LabelId,
212 property_key: PropertyKeyId,
213 ) -> Result<(), CatalogError> {
214 match &self.schema {
215 Some(schema) => schema.add_unique_constraint(label, property_key),
216 None => Err(CatalogError::SchemaNotEnabled),
217 }
218 }
219
220 pub fn add_required_property(
224 &self,
225 label: LabelId,
226 property_key: PropertyKeyId,
227 ) -> Result<(), CatalogError> {
228 match &self.schema {
229 Some(schema) => schema.add_required_property(label, property_key),
230 None => Err(CatalogError::SchemaNotEnabled),
231 }
232 }
233
234 #[must_use]
236 pub fn is_property_required(&self, label: LabelId, property_key: PropertyKeyId) -> bool {
237 self.schema
238 .as_ref()
239 .is_some_and(|s| s.is_property_required(label, property_key))
240 }
241
242 #[must_use]
244 pub fn is_property_unique(&self, label: LabelId, property_key: PropertyKeyId) -> bool {
245 self.schema
246 .as_ref()
247 .is_some_and(|s| s.is_property_unique(label, property_key))
248 }
249
250 #[must_use]
254 pub fn schema(&self) -> Option<&SchemaCatalog> {
255 self.schema.as_ref()
256 }
257
258 pub fn register_node_type(&self, def: NodeTypeDefinition) -> Result<(), CatalogError> {
260 match &self.schema {
261 Some(schema) => schema.register_node_type(def),
262 None => Err(CatalogError::SchemaNotEnabled),
263 }
264 }
265
266 pub fn register_or_replace_node_type(&self, def: NodeTypeDefinition) {
268 if let Some(schema) = &self.schema {
269 schema.register_or_replace_node_type(def);
270 }
271 }
272
273 pub fn drop_node_type(&self, name: &str) -> Result<(), CatalogError> {
275 match &self.schema {
276 Some(schema) => schema.drop_node_type(name),
277 None => Err(CatalogError::SchemaNotEnabled),
278 }
279 }
280
281 #[must_use]
283 pub fn get_node_type(&self, name: &str) -> Option<NodeTypeDefinition> {
284 self.schema.as_ref().and_then(|s| s.get_node_type(name))
285 }
286
287 #[must_use]
289 pub fn all_node_type_names(&self) -> Vec<String> {
290 self.schema
291 .as_ref()
292 .map(SchemaCatalog::all_node_types)
293 .unwrap_or_default()
294 }
295
296 pub fn register_edge_type_def(&self, def: EdgeTypeDefinition) -> Result<(), CatalogError> {
298 match &self.schema {
299 Some(schema) => schema.register_edge_type(def),
300 None => Err(CatalogError::SchemaNotEnabled),
301 }
302 }
303
304 pub fn register_or_replace_edge_type_def(&self, def: EdgeTypeDefinition) {
306 if let Some(schema) = &self.schema {
307 schema.register_or_replace_edge_type(def);
308 }
309 }
310
311 pub fn drop_edge_type_def(&self, name: &str) -> Result<(), CatalogError> {
313 match &self.schema {
314 Some(schema) => schema.drop_edge_type(name),
315 None => Err(CatalogError::SchemaNotEnabled),
316 }
317 }
318
319 #[must_use]
321 pub fn get_edge_type_def(&self, name: &str) -> Option<EdgeTypeDefinition> {
322 self.schema.as_ref().and_then(|s| s.get_edge_type(name))
323 }
324
325 pub fn register_graph_type(&self, def: GraphTypeDefinition) -> Result<(), CatalogError> {
327 match &self.schema {
328 Some(schema) => schema.register_graph_type(def),
329 None => Err(CatalogError::SchemaNotEnabled),
330 }
331 }
332
333 pub fn drop_graph_type(&self, name: &str) -> Result<(), CatalogError> {
335 match &self.schema {
336 Some(schema) => schema.drop_graph_type(name),
337 None => Err(CatalogError::SchemaNotEnabled),
338 }
339 }
340
341 pub fn register_schema_namespace(&self, name: String) -> Result<(), CatalogError> {
343 match &self.schema {
344 Some(schema) => schema.register_schema(name),
345 None => Err(CatalogError::SchemaNotEnabled),
346 }
347 }
348
349 pub fn drop_schema_namespace(&self, name: &str) -> Result<(), CatalogError> {
351 match &self.schema {
352 Some(schema) => schema.drop_schema(name),
353 None => Err(CatalogError::SchemaNotEnabled),
354 }
355 }
356
357 pub fn alter_node_type_add_property(
359 &self,
360 type_name: &str,
361 property: TypedProperty,
362 ) -> Result<(), CatalogError> {
363 match &self.schema {
364 Some(schema) => schema.alter_node_type_add_property(type_name, property),
365 None => Err(CatalogError::SchemaNotEnabled),
366 }
367 }
368
369 pub fn alter_node_type_drop_property(
371 &self,
372 type_name: &str,
373 property_name: &str,
374 ) -> Result<(), CatalogError> {
375 match &self.schema {
376 Some(schema) => schema.alter_node_type_drop_property(type_name, property_name),
377 None => Err(CatalogError::SchemaNotEnabled),
378 }
379 }
380
381 pub fn alter_edge_type_add_property(
383 &self,
384 type_name: &str,
385 property: TypedProperty,
386 ) -> Result<(), CatalogError> {
387 match &self.schema {
388 Some(schema) => schema.alter_edge_type_add_property(type_name, property),
389 None => Err(CatalogError::SchemaNotEnabled),
390 }
391 }
392
393 pub fn alter_edge_type_drop_property(
395 &self,
396 type_name: &str,
397 property_name: &str,
398 ) -> Result<(), CatalogError> {
399 match &self.schema {
400 Some(schema) => schema.alter_edge_type_drop_property(type_name, property_name),
401 None => Err(CatalogError::SchemaNotEnabled),
402 }
403 }
404
405 pub fn alter_graph_type_add_node_type(
407 &self,
408 graph_type_name: &str,
409 node_type: String,
410 ) -> Result<(), CatalogError> {
411 match &self.schema {
412 Some(schema) => schema.alter_graph_type_add_node_type(graph_type_name, node_type),
413 None => Err(CatalogError::SchemaNotEnabled),
414 }
415 }
416
417 pub fn alter_graph_type_drop_node_type(
419 &self,
420 graph_type_name: &str,
421 node_type: &str,
422 ) -> Result<(), CatalogError> {
423 match &self.schema {
424 Some(schema) => schema.alter_graph_type_drop_node_type(graph_type_name, node_type),
425 None => Err(CatalogError::SchemaNotEnabled),
426 }
427 }
428
429 pub fn alter_graph_type_add_edge_type(
431 &self,
432 graph_type_name: &str,
433 edge_type: String,
434 ) -> Result<(), CatalogError> {
435 match &self.schema {
436 Some(schema) => schema.alter_graph_type_add_edge_type(graph_type_name, edge_type),
437 None => Err(CatalogError::SchemaNotEnabled),
438 }
439 }
440
441 pub fn alter_graph_type_drop_edge_type(
443 &self,
444 graph_type_name: &str,
445 edge_type: &str,
446 ) -> Result<(), CatalogError> {
447 match &self.schema {
448 Some(schema) => schema.alter_graph_type_drop_edge_type(graph_type_name, edge_type),
449 None => Err(CatalogError::SchemaNotEnabled),
450 }
451 }
452
453 pub fn bind_graph_type(
455 &self,
456 graph_name: &str,
457 graph_type: String,
458 ) -> Result<(), CatalogError> {
459 match &self.schema {
460 Some(schema) => {
461 if schema.get_graph_type(&graph_type).is_none() {
463 return Err(CatalogError::TypeNotFound(graph_type));
464 }
465 schema
466 .graph_type_bindings
467 .write()
468 .insert(graph_name.to_string(), graph_type);
469 Ok(())
470 }
471 None => Err(CatalogError::SchemaNotEnabled),
472 }
473 }
474
475 pub fn get_graph_type_binding(&self, graph_name: &str) -> Option<String> {
477 self.schema
478 .as_ref()?
479 .graph_type_bindings
480 .read()
481 .get(graph_name)
482 .cloned()
483 }
484
485 pub fn register_procedure(&self, def: ProcedureDefinition) -> Result<(), CatalogError> {
487 match &self.schema {
488 Some(schema) => schema.register_procedure(def),
489 None => Err(CatalogError::SchemaNotEnabled),
490 }
491 }
492
493 pub fn replace_procedure(&self, def: ProcedureDefinition) -> Result<(), CatalogError> {
495 match &self.schema {
496 Some(schema) => {
497 schema.replace_procedure(def);
498 Ok(())
499 }
500 None => Err(CatalogError::SchemaNotEnabled),
501 }
502 }
503
504 pub fn drop_procedure(&self, name: &str) -> Result<(), CatalogError> {
506 match &self.schema {
507 Some(schema) => schema.drop_procedure(name),
508 None => Err(CatalogError::SchemaNotEnabled),
509 }
510 }
511
512 pub fn get_procedure(&self, name: &str) -> Option<ProcedureDefinition> {
514 self.schema.as_ref()?.get_procedure(name)
515 }
516}
517
518impl Default for Catalog {
519 fn default() -> Self {
520 Self::new()
521 }
522}
523
524struct LabelCatalog {
528 name_to_id: RwLock<HashMap<Arc<str>, LabelId>>,
529 id_to_name: RwLock<Vec<Arc<str>>>,
530 next_id: AtomicU32,
531}
532
533impl LabelCatalog {
534 fn new() -> Self {
535 Self {
536 name_to_id: RwLock::new(HashMap::new()),
537 id_to_name: RwLock::new(Vec::new()),
538 next_id: AtomicU32::new(0),
539 }
540 }
541
542 fn get_or_create(&self, name: &str) -> LabelId {
543 {
545 let name_to_id = self.name_to_id.read();
546 if let Some(&id) = name_to_id.get(name) {
547 return id;
548 }
549 }
550
551 let mut name_to_id = self.name_to_id.write();
553 let mut id_to_name = self.id_to_name.write();
554
555 if let Some(&id) = name_to_id.get(name) {
557 return id;
558 }
559
560 let id = LabelId::new(self.next_id.fetch_add(1, Ordering::Relaxed));
561 let name: Arc<str> = name.into();
562 name_to_id.insert(Arc::clone(&name), id);
563 id_to_name.push(name);
564 id
565 }
566
567 fn get_id(&self, name: &str) -> Option<LabelId> {
568 self.name_to_id.read().get(name).copied()
569 }
570
571 fn get_name(&self, id: LabelId) -> Option<Arc<str>> {
572 self.id_to_name.read().get(id.as_u32() as usize).cloned()
573 }
574
575 fn count(&self) -> usize {
576 self.id_to_name.read().len()
577 }
578
579 fn all_names(&self) -> Vec<Arc<str>> {
580 self.id_to_name.read().clone()
581 }
582}
583
584struct PropertyCatalog {
588 name_to_id: RwLock<HashMap<Arc<str>, PropertyKeyId>>,
589 id_to_name: RwLock<Vec<Arc<str>>>,
590 next_id: AtomicU32,
591}
592
593impl PropertyCatalog {
594 fn new() -> Self {
595 Self {
596 name_to_id: RwLock::new(HashMap::new()),
597 id_to_name: RwLock::new(Vec::new()),
598 next_id: AtomicU32::new(0),
599 }
600 }
601
602 fn get_or_create(&self, name: &str) -> PropertyKeyId {
603 {
605 let name_to_id = self.name_to_id.read();
606 if let Some(&id) = name_to_id.get(name) {
607 return id;
608 }
609 }
610
611 let mut name_to_id = self.name_to_id.write();
613 let mut id_to_name = self.id_to_name.write();
614
615 if let Some(&id) = name_to_id.get(name) {
617 return id;
618 }
619
620 let id = PropertyKeyId::new(self.next_id.fetch_add(1, Ordering::Relaxed));
621 let name: Arc<str> = name.into();
622 name_to_id.insert(Arc::clone(&name), id);
623 id_to_name.push(name);
624 id
625 }
626
627 fn get_id(&self, name: &str) -> Option<PropertyKeyId> {
628 self.name_to_id.read().get(name).copied()
629 }
630
631 fn get_name(&self, id: PropertyKeyId) -> Option<Arc<str>> {
632 self.id_to_name.read().get(id.as_u32() as usize).cloned()
633 }
634
635 fn count(&self) -> usize {
636 self.id_to_name.read().len()
637 }
638
639 fn all_names(&self) -> Vec<Arc<str>> {
640 self.id_to_name.read().clone()
641 }
642}
643
644struct EdgeTypeCatalog {
648 name_to_id: RwLock<HashMap<Arc<str>, EdgeTypeId>>,
649 id_to_name: RwLock<Vec<Arc<str>>>,
650 next_id: AtomicU32,
651}
652
653impl EdgeTypeCatalog {
654 fn new() -> Self {
655 Self {
656 name_to_id: RwLock::new(HashMap::new()),
657 id_to_name: RwLock::new(Vec::new()),
658 next_id: AtomicU32::new(0),
659 }
660 }
661
662 fn get_or_create(&self, name: &str) -> EdgeTypeId {
663 {
665 let name_to_id = self.name_to_id.read();
666 if let Some(&id) = name_to_id.get(name) {
667 return id;
668 }
669 }
670
671 let mut name_to_id = self.name_to_id.write();
673 let mut id_to_name = self.id_to_name.write();
674
675 if let Some(&id) = name_to_id.get(name) {
677 return id;
678 }
679
680 let id = EdgeTypeId::new(self.next_id.fetch_add(1, Ordering::Relaxed));
681 let name: Arc<str> = name.into();
682 name_to_id.insert(Arc::clone(&name), id);
683 id_to_name.push(name);
684 id
685 }
686
687 fn get_id(&self, name: &str) -> Option<EdgeTypeId> {
688 self.name_to_id.read().get(name).copied()
689 }
690
691 fn get_name(&self, id: EdgeTypeId) -> Option<Arc<str>> {
692 self.id_to_name.read().get(id.as_u32() as usize).cloned()
693 }
694
695 fn count(&self) -> usize {
696 self.id_to_name.read().len()
697 }
698
699 fn all_names(&self) -> Vec<Arc<str>> {
700 self.id_to_name.read().clone()
701 }
702}
703
704#[derive(Debug, Clone, Copy, PartialEq, Eq)]
708pub enum IndexType {
709 Hash,
711 BTree,
713 FullText,
715}
716
717#[derive(Debug, Clone)]
719pub struct IndexDefinition {
720 pub id: IndexId,
722 pub label: LabelId,
724 pub property_key: PropertyKeyId,
726 pub index_type: IndexType,
728}
729
730struct IndexCatalog {
732 indexes: RwLock<HashMap<IndexId, IndexDefinition>>,
733 label_indexes: RwLock<HashMap<LabelId, Vec<IndexId>>>,
734 label_property_indexes: RwLock<HashMap<(LabelId, PropertyKeyId), Vec<IndexId>>>,
735 next_id: AtomicU32,
736}
737
738impl IndexCatalog {
739 fn new() -> Self {
740 Self {
741 indexes: RwLock::new(HashMap::new()),
742 label_indexes: RwLock::new(HashMap::new()),
743 label_property_indexes: RwLock::new(HashMap::new()),
744 next_id: AtomicU32::new(0),
745 }
746 }
747
748 fn create(
749 &self,
750 label: LabelId,
751 property_key: PropertyKeyId,
752 index_type: IndexType,
753 ) -> IndexId {
754 let id = IndexId::new(self.next_id.fetch_add(1, Ordering::Relaxed));
755 let definition = IndexDefinition {
756 id,
757 label,
758 property_key,
759 index_type,
760 };
761
762 let mut indexes = self.indexes.write();
763 let mut label_indexes = self.label_indexes.write();
764 let mut label_property_indexes = self.label_property_indexes.write();
765
766 indexes.insert(id, definition);
767 label_indexes.entry(label).or_default().push(id);
768 label_property_indexes
769 .entry((label, property_key))
770 .or_default()
771 .push(id);
772
773 id
774 }
775
776 fn drop(&self, id: IndexId) -> bool {
777 let mut indexes = self.indexes.write();
778 let mut label_indexes = self.label_indexes.write();
779 let mut label_property_indexes = self.label_property_indexes.write();
780
781 if let Some(definition) = indexes.remove(&id) {
782 if let Some(ids) = label_indexes.get_mut(&definition.label) {
784 ids.retain(|&i| i != id);
785 }
786 if let Some(ids) =
788 label_property_indexes.get_mut(&(definition.label, definition.property_key))
789 {
790 ids.retain(|&i| i != id);
791 }
792 true
793 } else {
794 false
795 }
796 }
797
798 fn get(&self, id: IndexId) -> Option<IndexDefinition> {
799 self.indexes.read().get(&id).cloned()
800 }
801
802 fn for_label(&self, label: LabelId) -> Vec<IndexId> {
803 self.label_indexes
804 .read()
805 .get(&label)
806 .cloned()
807 .unwrap_or_default()
808 }
809
810 fn for_label_property(&self, label: LabelId, property_key: PropertyKeyId) -> Vec<IndexId> {
811 self.label_property_indexes
812 .read()
813 .get(&(label, property_key))
814 .cloned()
815 .unwrap_or_default()
816 }
817
818 fn count(&self) -> usize {
819 self.indexes.read().len()
820 }
821}
822
823#[derive(Debug, Clone, PartialEq, Eq)]
827pub enum PropertyDataType {
828 String,
830 Int64,
832 Float64,
834 Bool,
836 Date,
838 Time,
840 Timestamp,
842 Duration,
844 List,
846 Map,
848 Bytes,
850 Any,
852}
853
854impl PropertyDataType {
855 #[must_use]
857 pub fn from_type_name(name: &str) -> Self {
858 match name.to_uppercase().as_str() {
859 "STRING" | "VARCHAR" | "TEXT" => Self::String,
860 "INT" | "INT64" | "INTEGER" | "BIGINT" => Self::Int64,
861 "FLOAT" | "FLOAT64" | "DOUBLE" | "REAL" => Self::Float64,
862 "BOOL" | "BOOLEAN" => Self::Bool,
863 "DATE" => Self::Date,
864 "TIME" => Self::Time,
865 "TIMESTAMP" | "DATETIME" => Self::Timestamp,
866 "DURATION" | "INTERVAL" => Self::Duration,
867 "LIST" | "ARRAY" => Self::List,
868 "MAP" | "RECORD" => Self::Map,
869 "BYTES" | "BINARY" | "BLOB" => Self::Bytes,
870 _ => Self::Any,
871 }
872 }
873
874 #[must_use]
876 pub fn matches(&self, value: &Value) -> bool {
877 matches!(
878 (self, value),
879 (Self::Any, _)
880 | (_, Value::Null)
881 | (Self::String, Value::String(_))
882 | (Self::Int64, Value::Int64(_))
883 | (Self::Float64, Value::Float64(_))
884 | (Self::Bool, Value::Bool(_))
885 | (Self::Date, Value::Date(_))
886 | (Self::Time, Value::Time(_))
887 | (Self::Timestamp, Value::Timestamp(_))
888 | (Self::Duration, Value::Duration(_))
889 | (Self::List, Value::List(_))
890 | (Self::Bytes, Value::Bytes(_))
891 )
892 }
893}
894
895impl std::fmt::Display for PropertyDataType {
896 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
897 match self {
898 Self::String => write!(f, "STRING"),
899 Self::Int64 => write!(f, "INT64"),
900 Self::Float64 => write!(f, "FLOAT64"),
901 Self::Bool => write!(f, "BOOLEAN"),
902 Self::Date => write!(f, "DATE"),
903 Self::Time => write!(f, "TIME"),
904 Self::Timestamp => write!(f, "TIMESTAMP"),
905 Self::Duration => write!(f, "DURATION"),
906 Self::List => write!(f, "LIST"),
907 Self::Map => write!(f, "MAP"),
908 Self::Bytes => write!(f, "BYTES"),
909 Self::Any => write!(f, "ANY"),
910 }
911 }
912}
913
914#[derive(Debug, Clone)]
916pub struct TypedProperty {
917 pub name: String,
919 pub data_type: PropertyDataType,
921 pub nullable: bool,
923 pub default_value: Option<Value>,
925}
926
927#[derive(Debug, Clone)]
929pub enum TypeConstraint {
930 PrimaryKey(Vec<String>),
932 Unique(Vec<String>),
934 NotNull(String),
936 Check {
938 name: Option<String>,
940 expression: String,
942 },
943}
944
945#[derive(Debug, Clone)]
947pub struct NodeTypeDefinition {
948 pub name: String,
950 pub properties: Vec<TypedProperty>,
952 pub constraints: Vec<TypeConstraint>,
954}
955
956#[derive(Debug, Clone)]
958pub struct EdgeTypeDefinition {
959 pub name: String,
961 pub properties: Vec<TypedProperty>,
963 pub constraints: Vec<TypeConstraint>,
965}
966
967#[derive(Debug, Clone)]
969pub struct GraphTypeDefinition {
970 pub name: String,
972 pub allowed_node_types: Vec<String>,
974 pub allowed_edge_types: Vec<String>,
976 pub open: bool,
978}
979
980#[derive(Debug, Clone)]
982pub struct ProcedureDefinition {
983 pub name: String,
985 pub params: Vec<(String, String)>,
987 pub returns: Vec<(String, String)>,
989 pub body: String,
991}
992
993pub struct SchemaCatalog {
997 unique_constraints: RwLock<HashMap<(LabelId, PropertyKeyId), ()>>,
999 required_properties: RwLock<HashMap<(LabelId, PropertyKeyId), ()>>,
1001 node_types: RwLock<HashMap<String, NodeTypeDefinition>>,
1003 edge_types: RwLock<HashMap<String, EdgeTypeDefinition>>,
1005 graph_types: RwLock<HashMap<String, GraphTypeDefinition>>,
1007 schemas: RwLock<Vec<String>>,
1009 graph_type_bindings: RwLock<HashMap<String, String>>,
1011 procedures: RwLock<HashMap<String, ProcedureDefinition>>,
1013}
1014
1015impl SchemaCatalog {
1016 fn new() -> Self {
1017 Self {
1018 unique_constraints: RwLock::new(HashMap::new()),
1019 required_properties: RwLock::new(HashMap::new()),
1020 node_types: RwLock::new(HashMap::new()),
1021 edge_types: RwLock::new(HashMap::new()),
1022 graph_types: RwLock::new(HashMap::new()),
1023 schemas: RwLock::new(Vec::new()),
1024 graph_type_bindings: RwLock::new(HashMap::new()),
1025 procedures: RwLock::new(HashMap::new()),
1026 }
1027 }
1028
1029 pub fn register_node_type(&self, def: NodeTypeDefinition) -> Result<(), CatalogError> {
1033 let mut types = self.node_types.write();
1034 if types.contains_key(&def.name) {
1035 return Err(CatalogError::TypeAlreadyExists(def.name));
1036 }
1037 types.insert(def.name.clone(), def);
1038 Ok(())
1039 }
1040
1041 pub fn register_or_replace_node_type(&self, def: NodeTypeDefinition) {
1043 self.node_types.write().insert(def.name.clone(), def);
1044 }
1045
1046 pub fn drop_node_type(&self, name: &str) -> Result<(), CatalogError> {
1048 let mut types = self.node_types.write();
1049 if types.remove(name).is_none() {
1050 return Err(CatalogError::TypeNotFound(name.to_string()));
1051 }
1052 Ok(())
1053 }
1054
1055 #[must_use]
1057 pub fn get_node_type(&self, name: &str) -> Option<NodeTypeDefinition> {
1058 self.node_types.read().get(name).cloned()
1059 }
1060
1061 #[must_use]
1063 pub fn all_node_types(&self) -> Vec<String> {
1064 self.node_types.read().keys().cloned().collect()
1065 }
1066
1067 pub fn register_edge_type(&self, def: EdgeTypeDefinition) -> Result<(), CatalogError> {
1071 let mut types = self.edge_types.write();
1072 if types.contains_key(&def.name) {
1073 return Err(CatalogError::TypeAlreadyExists(def.name));
1074 }
1075 types.insert(def.name.clone(), def);
1076 Ok(())
1077 }
1078
1079 pub fn register_or_replace_edge_type(&self, def: EdgeTypeDefinition) {
1081 self.edge_types.write().insert(def.name.clone(), def);
1082 }
1083
1084 pub fn drop_edge_type(&self, name: &str) -> Result<(), CatalogError> {
1086 let mut types = self.edge_types.write();
1087 if types.remove(name).is_none() {
1088 return Err(CatalogError::TypeNotFound(name.to_string()));
1089 }
1090 Ok(())
1091 }
1092
1093 #[must_use]
1095 pub fn get_edge_type(&self, name: &str) -> Option<EdgeTypeDefinition> {
1096 self.edge_types.read().get(name).cloned()
1097 }
1098
1099 #[must_use]
1101 pub fn all_edge_types(&self) -> Vec<String> {
1102 self.edge_types.read().keys().cloned().collect()
1103 }
1104
1105 pub fn register_graph_type(&self, def: GraphTypeDefinition) -> Result<(), CatalogError> {
1109 let mut types = self.graph_types.write();
1110 if types.contains_key(&def.name) {
1111 return Err(CatalogError::TypeAlreadyExists(def.name));
1112 }
1113 types.insert(def.name.clone(), def);
1114 Ok(())
1115 }
1116
1117 pub fn drop_graph_type(&self, name: &str) -> Result<(), CatalogError> {
1119 let mut types = self.graph_types.write();
1120 if types.remove(name).is_none() {
1121 return Err(CatalogError::TypeNotFound(name.to_string()));
1122 }
1123 Ok(())
1124 }
1125
1126 #[must_use]
1128 pub fn get_graph_type(&self, name: &str) -> Option<GraphTypeDefinition> {
1129 self.graph_types.read().get(name).cloned()
1130 }
1131
1132 pub fn register_schema(&self, name: String) -> Result<(), CatalogError> {
1136 let mut schemas = self.schemas.write();
1137 if schemas.contains(&name) {
1138 return Err(CatalogError::SchemaAlreadyExists(name));
1139 }
1140 schemas.push(name);
1141 Ok(())
1142 }
1143
1144 pub fn drop_schema(&self, name: &str) -> Result<(), CatalogError> {
1146 let mut schemas = self.schemas.write();
1147 if let Some(pos) = schemas.iter().position(|s| s == name) {
1148 schemas.remove(pos);
1149 Ok(())
1150 } else {
1151 Err(CatalogError::SchemaNotFound(name.to_string()))
1152 }
1153 }
1154
1155 pub fn alter_node_type_add_property(
1159 &self,
1160 type_name: &str,
1161 property: TypedProperty,
1162 ) -> Result<(), CatalogError> {
1163 let mut types = self.node_types.write();
1164 let def = types
1165 .get_mut(type_name)
1166 .ok_or_else(|| CatalogError::TypeNotFound(type_name.to_string()))?;
1167 if def.properties.iter().any(|p| p.name == property.name) {
1168 return Err(CatalogError::TypeAlreadyExists(format!(
1169 "property {} on {}",
1170 property.name, type_name
1171 )));
1172 }
1173 def.properties.push(property);
1174 Ok(())
1175 }
1176
1177 pub fn alter_node_type_drop_property(
1179 &self,
1180 type_name: &str,
1181 property_name: &str,
1182 ) -> Result<(), CatalogError> {
1183 let mut types = self.node_types.write();
1184 let def = types
1185 .get_mut(type_name)
1186 .ok_or_else(|| CatalogError::TypeNotFound(type_name.to_string()))?;
1187 let len_before = def.properties.len();
1188 def.properties.retain(|p| p.name != property_name);
1189 if def.properties.len() == len_before {
1190 return Err(CatalogError::TypeNotFound(format!(
1191 "property {} on {}",
1192 property_name, type_name
1193 )));
1194 }
1195 Ok(())
1196 }
1197
1198 pub fn alter_edge_type_add_property(
1200 &self,
1201 type_name: &str,
1202 property: TypedProperty,
1203 ) -> Result<(), CatalogError> {
1204 let mut types = self.edge_types.write();
1205 let def = types
1206 .get_mut(type_name)
1207 .ok_or_else(|| CatalogError::TypeNotFound(type_name.to_string()))?;
1208 if def.properties.iter().any(|p| p.name == property.name) {
1209 return Err(CatalogError::TypeAlreadyExists(format!(
1210 "property {} on {}",
1211 property.name, type_name
1212 )));
1213 }
1214 def.properties.push(property);
1215 Ok(())
1216 }
1217
1218 pub fn alter_edge_type_drop_property(
1220 &self,
1221 type_name: &str,
1222 property_name: &str,
1223 ) -> Result<(), CatalogError> {
1224 let mut types = self.edge_types.write();
1225 let def = types
1226 .get_mut(type_name)
1227 .ok_or_else(|| CatalogError::TypeNotFound(type_name.to_string()))?;
1228 let len_before = def.properties.len();
1229 def.properties.retain(|p| p.name != property_name);
1230 if def.properties.len() == len_before {
1231 return Err(CatalogError::TypeNotFound(format!(
1232 "property {} on {}",
1233 property_name, type_name
1234 )));
1235 }
1236 Ok(())
1237 }
1238
1239 pub fn alter_graph_type_add_node_type(
1241 &self,
1242 graph_type_name: &str,
1243 node_type: String,
1244 ) -> Result<(), CatalogError> {
1245 let mut types = self.graph_types.write();
1246 let def = types
1247 .get_mut(graph_type_name)
1248 .ok_or_else(|| CatalogError::TypeNotFound(graph_type_name.to_string()))?;
1249 if !def.allowed_node_types.contains(&node_type) {
1250 def.allowed_node_types.push(node_type);
1251 }
1252 Ok(())
1253 }
1254
1255 pub fn alter_graph_type_drop_node_type(
1257 &self,
1258 graph_type_name: &str,
1259 node_type: &str,
1260 ) -> Result<(), CatalogError> {
1261 let mut types = self.graph_types.write();
1262 let def = types
1263 .get_mut(graph_type_name)
1264 .ok_or_else(|| CatalogError::TypeNotFound(graph_type_name.to_string()))?;
1265 def.allowed_node_types.retain(|t| t != node_type);
1266 Ok(())
1267 }
1268
1269 pub fn alter_graph_type_add_edge_type(
1271 &self,
1272 graph_type_name: &str,
1273 edge_type: String,
1274 ) -> Result<(), CatalogError> {
1275 let mut types = self.graph_types.write();
1276 let def = types
1277 .get_mut(graph_type_name)
1278 .ok_or_else(|| CatalogError::TypeNotFound(graph_type_name.to_string()))?;
1279 if !def.allowed_edge_types.contains(&edge_type) {
1280 def.allowed_edge_types.push(edge_type);
1281 }
1282 Ok(())
1283 }
1284
1285 pub fn alter_graph_type_drop_edge_type(
1287 &self,
1288 graph_type_name: &str,
1289 edge_type: &str,
1290 ) -> Result<(), CatalogError> {
1291 let mut types = self.graph_types.write();
1292 let def = types
1293 .get_mut(graph_type_name)
1294 .ok_or_else(|| CatalogError::TypeNotFound(graph_type_name.to_string()))?;
1295 def.allowed_edge_types.retain(|t| t != edge_type);
1296 Ok(())
1297 }
1298
1299 pub fn register_procedure(&self, def: ProcedureDefinition) -> Result<(), CatalogError> {
1303 let mut procs = self.procedures.write();
1304 if procs.contains_key(&def.name) {
1305 return Err(CatalogError::TypeAlreadyExists(def.name.clone()));
1306 }
1307 procs.insert(def.name.clone(), def);
1308 Ok(())
1309 }
1310
1311 pub fn replace_procedure(&self, def: ProcedureDefinition) {
1313 self.procedures.write().insert(def.name.clone(), def);
1314 }
1315
1316 pub fn drop_procedure(&self, name: &str) -> Result<(), CatalogError> {
1318 let mut procs = self.procedures.write();
1319 if procs.remove(name).is_none() {
1320 return Err(CatalogError::TypeNotFound(name.to_string()));
1321 }
1322 Ok(())
1323 }
1324
1325 pub fn get_procedure(&self, name: &str) -> Option<ProcedureDefinition> {
1327 self.procedures.read().get(name).cloned()
1328 }
1329
1330 fn add_unique_constraint(
1331 &self,
1332 label: LabelId,
1333 property_key: PropertyKeyId,
1334 ) -> Result<(), CatalogError> {
1335 let mut constraints = self.unique_constraints.write();
1336 let key = (label, property_key);
1337 if constraints.contains_key(&key) {
1338 return Err(CatalogError::ConstraintAlreadyExists);
1339 }
1340 constraints.insert(key, ());
1341 Ok(())
1342 }
1343
1344 fn add_required_property(
1345 &self,
1346 label: LabelId,
1347 property_key: PropertyKeyId,
1348 ) -> Result<(), CatalogError> {
1349 let mut required = self.required_properties.write();
1350 let key = (label, property_key);
1351 if required.contains_key(&key) {
1352 return Err(CatalogError::ConstraintAlreadyExists);
1353 }
1354 required.insert(key, ());
1355 Ok(())
1356 }
1357
1358 fn is_property_required(&self, label: LabelId, property_key: PropertyKeyId) -> bool {
1359 self.required_properties
1360 .read()
1361 .contains_key(&(label, property_key))
1362 }
1363
1364 fn is_property_unique(&self, label: LabelId, property_key: PropertyKeyId) -> bool {
1365 self.unique_constraints
1366 .read()
1367 .contains_key(&(label, property_key))
1368 }
1369}
1370
1371#[derive(Debug, Clone, PartialEq, Eq)]
1375pub enum CatalogError {
1376 SchemaNotEnabled,
1378 ConstraintAlreadyExists,
1380 LabelNotFound(String),
1382 PropertyKeyNotFound(String),
1384 EdgeTypeNotFound(String),
1386 IndexNotFound(IndexId),
1388 TypeAlreadyExists(String),
1390 TypeNotFound(String),
1392 SchemaAlreadyExists(String),
1394 SchemaNotFound(String),
1396}
1397
1398impl std::fmt::Display for CatalogError {
1399 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1400 match self {
1401 Self::SchemaNotEnabled => write!(f, "Schema constraints are not enabled"),
1402 Self::ConstraintAlreadyExists => write!(f, "Constraint already exists"),
1403 Self::LabelNotFound(name) => write!(f, "Label not found: {name}"),
1404 Self::PropertyKeyNotFound(name) => write!(f, "Property key not found: {name}"),
1405 Self::EdgeTypeNotFound(name) => write!(f, "Edge type not found: {name}"),
1406 Self::IndexNotFound(id) => write!(f, "Index not found: {id}"),
1407 Self::TypeAlreadyExists(name) => write!(f, "Type already exists: {name}"),
1408 Self::TypeNotFound(name) => write!(f, "Type not found: {name}"),
1409 Self::SchemaAlreadyExists(name) => write!(f, "Schema already exists: {name}"),
1410 Self::SchemaNotFound(name) => write!(f, "Schema not found: {name}"),
1411 }
1412 }
1413}
1414
1415impl std::error::Error for CatalogError {}
1416
1417use grafeo_core::execution::operators::ConstraintValidator;
1420use grafeo_core::execution::operators::OperatorError;
1421
1422pub struct CatalogConstraintValidator {
1427 catalog: Arc<Catalog>,
1428}
1429
1430impl CatalogConstraintValidator {
1431 pub fn new(catalog: Arc<Catalog>) -> Self {
1433 Self { catalog }
1434 }
1435}
1436
1437impl ConstraintValidator for CatalogConstraintValidator {
1438 fn validate_node_property(
1439 &self,
1440 labels: &[String],
1441 key: &str,
1442 value: &Value,
1443 ) -> Result<(), OperatorError> {
1444 for label in labels {
1445 if let Some(type_def) = self.catalog.get_node_type(label)
1446 && let Some(typed_prop) = type_def.properties.iter().find(|p| p.name == key)
1447 {
1448 if !typed_prop.nullable && *value == Value::Null {
1450 return Err(OperatorError::ConstraintViolation(format!(
1451 "property '{key}' on :{label} is NOT NULL, cannot set to null"
1452 )));
1453 }
1454 if *value != Value::Null && !typed_prop.data_type.matches(value) {
1456 return Err(OperatorError::ConstraintViolation(format!(
1457 "property '{key}' on :{label} expects {:?}, got {:?}",
1458 typed_prop.data_type, value
1459 )));
1460 }
1461 }
1462 }
1463 Ok(())
1464 }
1465
1466 fn validate_node_complete(
1467 &self,
1468 labels: &[String],
1469 properties: &[(String, Value)],
1470 ) -> Result<(), OperatorError> {
1471 let prop_names: std::collections::HashSet<&str> =
1472 properties.iter().map(|(n, _)| n.as_str()).collect();
1473
1474 for label in labels {
1475 if let Some(type_def) = self.catalog.get_node_type(label) {
1476 for typed_prop in &type_def.properties {
1478 if !typed_prop.nullable
1479 && typed_prop.default_value.is_none()
1480 && !prop_names.contains(typed_prop.name.as_str())
1481 {
1482 return Err(OperatorError::ConstraintViolation(format!(
1483 "missing required property '{}' on :{label}",
1484 typed_prop.name
1485 )));
1486 }
1487 }
1488 for constraint in &type_def.constraints {
1490 match constraint {
1491 TypeConstraint::NotNull(prop_name) => {
1492 if !prop_names.contains(prop_name.as_str()) {
1493 return Err(OperatorError::ConstraintViolation(format!(
1494 "missing required property '{prop_name}' on :{label} (NOT NULL constraint)"
1495 )));
1496 }
1497 }
1498 TypeConstraint::PrimaryKey(key_props) => {
1499 for pk in key_props {
1500 if !prop_names.contains(pk.as_str()) {
1501 return Err(OperatorError::ConstraintViolation(format!(
1502 "missing primary key property '{pk}' on :{label}"
1503 )));
1504 }
1505 }
1506 }
1507 _ => {}
1508 }
1509 }
1510 }
1511 }
1512 Ok(())
1513 }
1514
1515 fn check_unique_node_property(
1516 &self,
1517 labels: &[String],
1518 key: &str,
1519 value: &Value,
1520 ) -> Result<(), OperatorError> {
1521 if *value == Value::Null {
1523 return Ok(());
1524 }
1525 for label in labels {
1526 if let Some(type_def) = self.catalog.get_node_type(label) {
1527 for constraint in &type_def.constraints {
1528 let is_unique = match constraint {
1529 TypeConstraint::Unique(props) => props.iter().any(|p| p == key),
1530 TypeConstraint::PrimaryKey(props) => props.iter().any(|p| p == key),
1531 _ => false,
1532 };
1533 if is_unique {
1534 let label_id = self.catalog.get_or_create_label(label);
1536 let prop_id = self.catalog.get_or_create_property_key(key);
1537 if self.catalog.is_property_unique(label_id, prop_id) {
1538 }
1544 }
1545 }
1546 }
1547 }
1548 Ok(())
1549 }
1550
1551 fn validate_edge_property(
1552 &self,
1553 edge_type: &str,
1554 key: &str,
1555 value: &Value,
1556 ) -> Result<(), OperatorError> {
1557 if let Some(type_def) = self.catalog.get_edge_type_def(edge_type)
1558 && let Some(typed_prop) = type_def.properties.iter().find(|p| p.name == key)
1559 {
1560 if !typed_prop.nullable && *value == Value::Null {
1562 return Err(OperatorError::ConstraintViolation(format!(
1563 "property '{key}' on :{edge_type} is NOT NULL, cannot set to null"
1564 )));
1565 }
1566 if *value != Value::Null && !typed_prop.data_type.matches(value) {
1568 return Err(OperatorError::ConstraintViolation(format!(
1569 "property '{key}' on :{edge_type} expects {:?}, got {:?}",
1570 typed_prop.data_type, value
1571 )));
1572 }
1573 }
1574 Ok(())
1575 }
1576
1577 fn validate_edge_complete(
1578 &self,
1579 edge_type: &str,
1580 properties: &[(String, Value)],
1581 ) -> Result<(), OperatorError> {
1582 if let Some(type_def) = self.catalog.get_edge_type_def(edge_type) {
1583 let prop_names: std::collections::HashSet<&str> =
1584 properties.iter().map(|(n, _)| n.as_str()).collect();
1585
1586 for typed_prop in &type_def.properties {
1587 if !typed_prop.nullable
1588 && typed_prop.default_value.is_none()
1589 && !prop_names.contains(typed_prop.name.as_str())
1590 {
1591 return Err(OperatorError::ConstraintViolation(format!(
1592 "missing required property '{}' on :{edge_type}",
1593 typed_prop.name
1594 )));
1595 }
1596 }
1597 }
1598 Ok(())
1599 }
1600}
1601
1602#[cfg(test)]
1603mod tests {
1604 use super::*;
1605 use std::thread;
1606
1607 #[test]
1608 fn test_catalog_labels() {
1609 let catalog = Catalog::new();
1610
1611 let person_id = catalog.get_or_create_label("Person");
1613 let company_id = catalog.get_or_create_label("Company");
1614
1615 assert_ne!(person_id, company_id);
1617
1618 assert_eq!(catalog.get_or_create_label("Person"), person_id);
1620
1621 assert_eq!(catalog.get_label_id("Person"), Some(person_id));
1623 assert_eq!(catalog.get_label_id("Company"), Some(company_id));
1624 assert_eq!(catalog.get_label_id("Unknown"), None);
1625
1626 assert_eq!(catalog.get_label_name(person_id).as_deref(), Some("Person"));
1628 assert_eq!(
1629 catalog.get_label_name(company_id).as_deref(),
1630 Some("Company")
1631 );
1632
1633 assert_eq!(catalog.label_count(), 2);
1635 }
1636
1637 #[test]
1638 fn test_catalog_property_keys() {
1639 let catalog = Catalog::new();
1640
1641 let name_id = catalog.get_or_create_property_key("name");
1642 let age_id = catalog.get_or_create_property_key("age");
1643
1644 assert_ne!(name_id, age_id);
1645 assert_eq!(catalog.get_or_create_property_key("name"), name_id);
1646 assert_eq!(catalog.get_property_key_id("name"), Some(name_id));
1647 assert_eq!(
1648 catalog.get_property_key_name(name_id).as_deref(),
1649 Some("name")
1650 );
1651 assert_eq!(catalog.property_key_count(), 2);
1652 }
1653
1654 #[test]
1655 fn test_catalog_edge_types() {
1656 let catalog = Catalog::new();
1657
1658 let knows_id = catalog.get_or_create_edge_type("KNOWS");
1659 let works_at_id = catalog.get_or_create_edge_type("WORKS_AT");
1660
1661 assert_ne!(knows_id, works_at_id);
1662 assert_eq!(catalog.get_or_create_edge_type("KNOWS"), knows_id);
1663 assert_eq!(catalog.get_edge_type_id("KNOWS"), Some(knows_id));
1664 assert_eq!(
1665 catalog.get_edge_type_name(knows_id).as_deref(),
1666 Some("KNOWS")
1667 );
1668 assert_eq!(catalog.edge_type_count(), 2);
1669 }
1670
1671 #[test]
1672 fn test_catalog_indexes() {
1673 let catalog = Catalog::new();
1674
1675 let person_id = catalog.get_or_create_label("Person");
1676 let name_id = catalog.get_or_create_property_key("name");
1677 let age_id = catalog.get_or_create_property_key("age");
1678
1679 let idx1 = catalog.create_index(person_id, name_id, IndexType::Hash);
1681 let idx2 = catalog.create_index(person_id, age_id, IndexType::BTree);
1682
1683 assert_ne!(idx1, idx2);
1684 assert_eq!(catalog.index_count(), 2);
1685
1686 let label_indexes = catalog.indexes_for_label(person_id);
1688 assert_eq!(label_indexes.len(), 2);
1689 assert!(label_indexes.contains(&idx1));
1690 assert!(label_indexes.contains(&idx2));
1691
1692 let name_indexes = catalog.indexes_for_label_property(person_id, name_id);
1694 assert_eq!(name_indexes.len(), 1);
1695 assert_eq!(name_indexes[0], idx1);
1696
1697 let def = catalog.get_index(idx1).unwrap();
1699 assert_eq!(def.label, person_id);
1700 assert_eq!(def.property_key, name_id);
1701 assert_eq!(def.index_type, IndexType::Hash);
1702
1703 assert!(catalog.drop_index(idx1));
1705 assert_eq!(catalog.index_count(), 1);
1706 assert!(catalog.get_index(idx1).is_none());
1707 assert_eq!(catalog.indexes_for_label(person_id).len(), 1);
1708 }
1709
1710 #[test]
1711 fn test_catalog_schema_constraints() {
1712 let catalog = Catalog::with_schema();
1713
1714 let person_id = catalog.get_or_create_label("Person");
1715 let email_id = catalog.get_or_create_property_key("email");
1716 let name_id = catalog.get_or_create_property_key("name");
1717
1718 assert!(catalog.add_unique_constraint(person_id, email_id).is_ok());
1720 assert!(catalog.add_required_property(person_id, name_id).is_ok());
1721
1722 assert!(catalog.is_property_unique(person_id, email_id));
1724 assert!(!catalog.is_property_unique(person_id, name_id));
1725 assert!(catalog.is_property_required(person_id, name_id));
1726 assert!(!catalog.is_property_required(person_id, email_id));
1727
1728 assert_eq!(
1730 catalog.add_unique_constraint(person_id, email_id),
1731 Err(CatalogError::ConstraintAlreadyExists)
1732 );
1733 }
1734
1735 #[test]
1736 fn test_catalog_schema_always_enabled() {
1737 let catalog = Catalog::new();
1739 assert!(catalog.has_schema());
1740
1741 let person_id = catalog.get_or_create_label("Person");
1742 let email_id = catalog.get_or_create_property_key("email");
1743
1744 assert_eq!(catalog.add_unique_constraint(person_id, email_id), Ok(()));
1746 }
1747
1748 #[test]
1751 fn test_catalog_default() {
1752 let catalog = Catalog::default();
1753 assert!(catalog.has_schema());
1754 assert_eq!(catalog.label_count(), 0);
1755 assert_eq!(catalog.property_key_count(), 0);
1756 assert_eq!(catalog.edge_type_count(), 0);
1757 assert_eq!(catalog.index_count(), 0);
1758 }
1759
1760 #[test]
1761 fn test_catalog_all_labels() {
1762 let catalog = Catalog::new();
1763
1764 catalog.get_or_create_label("Person");
1765 catalog.get_or_create_label("Company");
1766 catalog.get_or_create_label("Product");
1767
1768 let all = catalog.all_labels();
1769 assert_eq!(all.len(), 3);
1770 assert!(all.iter().any(|l| l.as_ref() == "Person"));
1771 assert!(all.iter().any(|l| l.as_ref() == "Company"));
1772 assert!(all.iter().any(|l| l.as_ref() == "Product"));
1773 }
1774
1775 #[test]
1776 fn test_catalog_all_property_keys() {
1777 let catalog = Catalog::new();
1778
1779 catalog.get_or_create_property_key("name");
1780 catalog.get_or_create_property_key("age");
1781 catalog.get_or_create_property_key("email");
1782
1783 let all = catalog.all_property_keys();
1784 assert_eq!(all.len(), 3);
1785 assert!(all.iter().any(|k| k.as_ref() == "name"));
1786 assert!(all.iter().any(|k| k.as_ref() == "age"));
1787 assert!(all.iter().any(|k| k.as_ref() == "email"));
1788 }
1789
1790 #[test]
1791 fn test_catalog_all_edge_types() {
1792 let catalog = Catalog::new();
1793
1794 catalog.get_or_create_edge_type("KNOWS");
1795 catalog.get_or_create_edge_type("WORKS_AT");
1796 catalog.get_or_create_edge_type("LIVES_IN");
1797
1798 let all = catalog.all_edge_types();
1799 assert_eq!(all.len(), 3);
1800 assert!(all.iter().any(|t| t.as_ref() == "KNOWS"));
1801 assert!(all.iter().any(|t| t.as_ref() == "WORKS_AT"));
1802 assert!(all.iter().any(|t| t.as_ref() == "LIVES_IN"));
1803 }
1804
1805 #[test]
1806 fn test_catalog_invalid_id_lookup() {
1807 let catalog = Catalog::new();
1808
1809 let _ = catalog.get_or_create_label("Person");
1811
1812 let invalid_label = LabelId::new(999);
1814 let invalid_property = PropertyKeyId::new(999);
1815 let invalid_edge_type = EdgeTypeId::new(999);
1816 let invalid_index = IndexId::new(999);
1817
1818 assert!(catalog.get_label_name(invalid_label).is_none());
1819 assert!(catalog.get_property_key_name(invalid_property).is_none());
1820 assert!(catalog.get_edge_type_name(invalid_edge_type).is_none());
1821 assert!(catalog.get_index(invalid_index).is_none());
1822 }
1823
1824 #[test]
1825 fn test_catalog_drop_nonexistent_index() {
1826 let catalog = Catalog::new();
1827 let invalid_index = IndexId::new(999);
1828 assert!(!catalog.drop_index(invalid_index));
1829 }
1830
1831 #[test]
1832 fn test_catalog_indexes_for_nonexistent_label() {
1833 let catalog = Catalog::new();
1834 let invalid_label = LabelId::new(999);
1835 let invalid_property = PropertyKeyId::new(999);
1836
1837 assert!(catalog.indexes_for_label(invalid_label).is_empty());
1838 assert!(
1839 catalog
1840 .indexes_for_label_property(invalid_label, invalid_property)
1841 .is_empty()
1842 );
1843 }
1844
1845 #[test]
1846 fn test_catalog_multiple_indexes_same_property() {
1847 let catalog = Catalog::new();
1848
1849 let person_id = catalog.get_or_create_label("Person");
1850 let name_id = catalog.get_or_create_property_key("name");
1851
1852 let hash_idx = catalog.create_index(person_id, name_id, IndexType::Hash);
1854 let btree_idx = catalog.create_index(person_id, name_id, IndexType::BTree);
1855 let fulltext_idx = catalog.create_index(person_id, name_id, IndexType::FullText);
1856
1857 assert_eq!(catalog.index_count(), 3);
1858
1859 let indexes = catalog.indexes_for_label_property(person_id, name_id);
1860 assert_eq!(indexes.len(), 3);
1861 assert!(indexes.contains(&hash_idx));
1862 assert!(indexes.contains(&btree_idx));
1863 assert!(indexes.contains(&fulltext_idx));
1864
1865 assert_eq!(
1867 catalog.get_index(hash_idx).unwrap().index_type,
1868 IndexType::Hash
1869 );
1870 assert_eq!(
1871 catalog.get_index(btree_idx).unwrap().index_type,
1872 IndexType::BTree
1873 );
1874 assert_eq!(
1875 catalog.get_index(fulltext_idx).unwrap().index_type,
1876 IndexType::FullText
1877 );
1878 }
1879
1880 #[test]
1881 fn test_catalog_schema_required_property_duplicate() {
1882 let catalog = Catalog::with_schema();
1883
1884 let person_id = catalog.get_or_create_label("Person");
1885 let name_id = catalog.get_or_create_property_key("name");
1886
1887 assert!(catalog.add_required_property(person_id, name_id).is_ok());
1889
1890 assert_eq!(
1892 catalog.add_required_property(person_id, name_id),
1893 Err(CatalogError::ConstraintAlreadyExists)
1894 );
1895 }
1896
1897 #[test]
1898 fn test_catalog_schema_check_without_constraints() {
1899 let catalog = Catalog::new();
1900
1901 let person_id = catalog.get_or_create_label("Person");
1902 let name_id = catalog.get_or_create_property_key("name");
1903
1904 assert!(!catalog.is_property_unique(person_id, name_id));
1906 assert!(!catalog.is_property_required(person_id, name_id));
1907 }
1908
1909 #[test]
1910 fn test_catalog_has_schema() {
1911 let catalog = Catalog::new();
1913 assert!(catalog.has_schema());
1914
1915 let with_schema = Catalog::with_schema();
1916 assert!(with_schema.has_schema());
1917 }
1918
1919 #[test]
1920 fn test_catalog_error_display() {
1921 assert_eq!(
1922 CatalogError::SchemaNotEnabled.to_string(),
1923 "Schema constraints are not enabled"
1924 );
1925 assert_eq!(
1926 CatalogError::ConstraintAlreadyExists.to_string(),
1927 "Constraint already exists"
1928 );
1929 assert_eq!(
1930 CatalogError::LabelNotFound("Person".to_string()).to_string(),
1931 "Label not found: Person"
1932 );
1933 assert_eq!(
1934 CatalogError::PropertyKeyNotFound("name".to_string()).to_string(),
1935 "Property key not found: name"
1936 );
1937 assert_eq!(
1938 CatalogError::EdgeTypeNotFound("KNOWS".to_string()).to_string(),
1939 "Edge type not found: KNOWS"
1940 );
1941 let idx = IndexId::new(42);
1942 assert!(CatalogError::IndexNotFound(idx).to_string().contains("42"));
1943 }
1944
1945 #[test]
1946 fn test_catalog_concurrent_label_creation() {
1947 use std::sync::Arc;
1948
1949 let catalog = Arc::new(Catalog::new());
1950 let mut handles = vec![];
1951
1952 for i in 0..10 {
1954 let catalog = Arc::clone(&catalog);
1955 handles.push(thread::spawn(move || {
1956 let label_name = format!("Label{}", i % 3); catalog.get_or_create_label(&label_name)
1958 }));
1959 }
1960
1961 let mut ids: Vec<LabelId> = handles.into_iter().map(|h| h.join().unwrap()).collect();
1962 ids.sort_by_key(|id| id.as_u32());
1963 ids.dedup();
1964
1965 assert_eq!(ids.len(), 3);
1967 assert_eq!(catalog.label_count(), 3);
1968 }
1969
1970 #[test]
1971 fn test_catalog_concurrent_property_key_creation() {
1972 use std::sync::Arc;
1973
1974 let catalog = Arc::new(Catalog::new());
1975 let mut handles = vec![];
1976
1977 for i in 0..10 {
1978 let catalog = Arc::clone(&catalog);
1979 handles.push(thread::spawn(move || {
1980 let key_name = format!("key{}", i % 4);
1981 catalog.get_or_create_property_key(&key_name)
1982 }));
1983 }
1984
1985 let mut ids: Vec<PropertyKeyId> = handles.into_iter().map(|h| h.join().unwrap()).collect();
1986 ids.sort_by_key(|id| id.as_u32());
1987 ids.dedup();
1988
1989 assert_eq!(ids.len(), 4);
1990 assert_eq!(catalog.property_key_count(), 4);
1991 }
1992
1993 #[test]
1994 fn test_catalog_concurrent_index_operations() {
1995 use std::sync::Arc;
1996
1997 let catalog = Arc::new(Catalog::new());
1998 let label = catalog.get_or_create_label("Node");
1999
2000 let mut handles = vec![];
2001
2002 for i in 0..5 {
2004 let catalog = Arc::clone(&catalog);
2005 handles.push(thread::spawn(move || {
2006 let prop = PropertyKeyId::new(i);
2007 catalog.create_index(label, prop, IndexType::Hash)
2008 }));
2009 }
2010
2011 let ids: Vec<IndexId> = handles.into_iter().map(|h| h.join().unwrap()).collect();
2012 assert_eq!(ids.len(), 5);
2013 assert_eq!(catalog.index_count(), 5);
2014 }
2015
2016 #[test]
2017 fn test_catalog_special_characters_in_names() {
2018 let catalog = Catalog::new();
2019
2020 let label1 = catalog.get_or_create_label("Label With Spaces");
2022 let label2 = catalog.get_or_create_label("Label-With-Dashes");
2023 let label3 = catalog.get_or_create_label("Label_With_Underscores");
2024 let label4 = catalog.get_or_create_label("LabelWithUnicode\u{00E9}");
2025
2026 assert_ne!(label1, label2);
2027 assert_ne!(label2, label3);
2028 assert_ne!(label3, label4);
2029
2030 assert_eq!(
2031 catalog.get_label_name(label1).as_deref(),
2032 Some("Label With Spaces")
2033 );
2034 assert_eq!(
2035 catalog.get_label_name(label4).as_deref(),
2036 Some("LabelWithUnicode\u{00E9}")
2037 );
2038 }
2039
2040 #[test]
2041 fn test_catalog_empty_names() {
2042 let catalog = Catalog::new();
2043
2044 let empty_label = catalog.get_or_create_label("");
2046 let empty_prop = catalog.get_or_create_property_key("");
2047 let empty_edge = catalog.get_or_create_edge_type("");
2048
2049 assert_eq!(catalog.get_label_name(empty_label).as_deref(), Some(""));
2050 assert_eq!(
2051 catalog.get_property_key_name(empty_prop).as_deref(),
2052 Some("")
2053 );
2054 assert_eq!(catalog.get_edge_type_name(empty_edge).as_deref(), Some(""));
2055
2056 assert_eq!(catalog.get_or_create_label(""), empty_label);
2058 }
2059
2060 #[test]
2061 fn test_catalog_large_number_of_entries() {
2062 let catalog = Catalog::new();
2063
2064 for i in 0..1000 {
2066 catalog.get_or_create_label(&format!("Label{}", i));
2067 }
2068
2069 assert_eq!(catalog.label_count(), 1000);
2070
2071 let all = catalog.all_labels();
2073 assert_eq!(all.len(), 1000);
2074
2075 let id = catalog.get_label_id("Label500").unwrap();
2077 assert_eq!(catalog.get_label_name(id).as_deref(), Some("Label500"));
2078 }
2079
2080 #[test]
2081 fn test_index_definition_debug() {
2082 let def = IndexDefinition {
2083 id: IndexId::new(1),
2084 label: LabelId::new(2),
2085 property_key: PropertyKeyId::new(3),
2086 index_type: IndexType::Hash,
2087 };
2088
2089 let debug_str = format!("{:?}", def);
2091 assert!(debug_str.contains("IndexDefinition"));
2092 assert!(debug_str.contains("Hash"));
2093 }
2094
2095 #[test]
2096 fn test_index_type_equality() {
2097 assert_eq!(IndexType::Hash, IndexType::Hash);
2098 assert_ne!(IndexType::Hash, IndexType::BTree);
2099 assert_ne!(IndexType::BTree, IndexType::FullText);
2100
2101 let t = IndexType::Hash;
2103 let t2 = t;
2104 assert_eq!(t, t2);
2105 }
2106
2107 #[test]
2108 fn test_catalog_error_equality() {
2109 assert_eq!(
2110 CatalogError::SchemaNotEnabled,
2111 CatalogError::SchemaNotEnabled
2112 );
2113 assert_eq!(
2114 CatalogError::ConstraintAlreadyExists,
2115 CatalogError::ConstraintAlreadyExists
2116 );
2117 assert_eq!(
2118 CatalogError::LabelNotFound("X".to_string()),
2119 CatalogError::LabelNotFound("X".to_string())
2120 );
2121 assert_ne!(
2122 CatalogError::LabelNotFound("X".to_string()),
2123 CatalogError::LabelNotFound("Y".to_string())
2124 );
2125 }
2126}