1use std::collections::{HashMap, HashSet};
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 all_indexes(&self) -> Vec<IndexDefinition> {
195 self.indexes.all()
196 }
197
198 #[must_use]
200 pub fn index_count(&self) -> usize {
201 self.indexes.count()
202 }
203
204 #[must_use]
208 pub fn has_schema(&self) -> bool {
209 self.schema.is_some()
210 }
211
212 pub fn add_unique_constraint(
216 &self,
217 label: LabelId,
218 property_key: PropertyKeyId,
219 ) -> Result<(), CatalogError> {
220 match &self.schema {
221 Some(schema) => schema.add_unique_constraint(label, property_key),
222 None => Err(CatalogError::SchemaNotEnabled),
223 }
224 }
225
226 pub fn add_required_property(
230 &self,
231 label: LabelId,
232 property_key: PropertyKeyId,
233 ) -> Result<(), CatalogError> {
234 match &self.schema {
235 Some(schema) => schema.add_required_property(label, property_key),
236 None => Err(CatalogError::SchemaNotEnabled),
237 }
238 }
239
240 #[must_use]
242 pub fn is_property_required(&self, label: LabelId, property_key: PropertyKeyId) -> bool {
243 self.schema
244 .as_ref()
245 .is_some_and(|s| s.is_property_required(label, property_key))
246 }
247
248 #[must_use]
250 pub fn is_property_unique(&self, label: LabelId, property_key: PropertyKeyId) -> bool {
251 self.schema
252 .as_ref()
253 .is_some_and(|s| s.is_property_unique(label, property_key))
254 }
255
256 #[must_use]
260 pub fn schema(&self) -> Option<&SchemaCatalog> {
261 self.schema.as_ref()
262 }
263
264 pub fn register_node_type(&self, def: NodeTypeDefinition) -> Result<(), CatalogError> {
266 match &self.schema {
267 Some(schema) => schema.register_node_type(def),
268 None => Err(CatalogError::SchemaNotEnabled),
269 }
270 }
271
272 pub fn register_or_replace_node_type(&self, def: NodeTypeDefinition) {
274 if let Some(schema) = &self.schema {
275 schema.register_or_replace_node_type(def);
276 }
277 }
278
279 pub fn drop_node_type(&self, name: &str) -> Result<(), CatalogError> {
281 match &self.schema {
282 Some(schema) => schema.drop_node_type(name),
283 None => Err(CatalogError::SchemaNotEnabled),
284 }
285 }
286
287 #[must_use]
289 pub fn get_node_type(&self, name: &str) -> Option<NodeTypeDefinition> {
290 self.schema.as_ref().and_then(|s| s.get_node_type(name))
291 }
292
293 #[must_use]
295 pub fn all_node_type_names(&self) -> Vec<String> {
296 self.schema
297 .as_ref()
298 .map(SchemaCatalog::all_node_types)
299 .unwrap_or_default()
300 }
301
302 #[must_use]
304 pub fn all_edge_type_names(&self) -> Vec<String> {
305 self.schema
306 .as_ref()
307 .map(SchemaCatalog::all_edge_types)
308 .unwrap_or_default()
309 }
310
311 pub fn register_edge_type_def(&self, def: EdgeTypeDefinition) -> Result<(), CatalogError> {
313 match &self.schema {
314 Some(schema) => schema.register_edge_type(def),
315 None => Err(CatalogError::SchemaNotEnabled),
316 }
317 }
318
319 pub fn register_or_replace_edge_type_def(&self, def: EdgeTypeDefinition) {
321 if let Some(schema) = &self.schema {
322 schema.register_or_replace_edge_type(def);
323 }
324 }
325
326 pub fn drop_edge_type_def(&self, name: &str) -> Result<(), CatalogError> {
328 match &self.schema {
329 Some(schema) => schema.drop_edge_type(name),
330 None => Err(CatalogError::SchemaNotEnabled),
331 }
332 }
333
334 #[must_use]
336 pub fn get_edge_type_def(&self, name: &str) -> Option<EdgeTypeDefinition> {
337 self.schema.as_ref().and_then(|s| s.get_edge_type(name))
338 }
339
340 pub fn register_graph_type(&self, def: GraphTypeDefinition) -> Result<(), CatalogError> {
342 match &self.schema {
343 Some(schema) => schema.register_graph_type(def),
344 None => Err(CatalogError::SchemaNotEnabled),
345 }
346 }
347
348 pub fn drop_graph_type(&self, name: &str) -> Result<(), CatalogError> {
350 match &self.schema {
351 Some(schema) => schema.drop_graph_type(name),
352 None => Err(CatalogError::SchemaNotEnabled),
353 }
354 }
355
356 pub fn register_schema_namespace(&self, name: String) -> Result<(), CatalogError> {
358 match &self.schema {
359 Some(schema) => schema.register_schema(name),
360 None => Err(CatalogError::SchemaNotEnabled),
361 }
362 }
363
364 pub fn drop_schema_namespace(&self, name: &str) -> Result<(), CatalogError> {
366 match &self.schema {
367 Some(schema) => schema.drop_schema(name),
368 None => Err(CatalogError::SchemaNotEnabled),
369 }
370 }
371
372 pub fn alter_node_type_add_property(
374 &self,
375 type_name: &str,
376 property: TypedProperty,
377 ) -> Result<(), CatalogError> {
378 match &self.schema {
379 Some(schema) => schema.alter_node_type_add_property(type_name, property),
380 None => Err(CatalogError::SchemaNotEnabled),
381 }
382 }
383
384 pub fn alter_node_type_drop_property(
386 &self,
387 type_name: &str,
388 property_name: &str,
389 ) -> Result<(), CatalogError> {
390 match &self.schema {
391 Some(schema) => schema.alter_node_type_drop_property(type_name, property_name),
392 None => Err(CatalogError::SchemaNotEnabled),
393 }
394 }
395
396 pub fn alter_edge_type_add_property(
398 &self,
399 type_name: &str,
400 property: TypedProperty,
401 ) -> Result<(), CatalogError> {
402 match &self.schema {
403 Some(schema) => schema.alter_edge_type_add_property(type_name, property),
404 None => Err(CatalogError::SchemaNotEnabled),
405 }
406 }
407
408 pub fn alter_edge_type_drop_property(
410 &self,
411 type_name: &str,
412 property_name: &str,
413 ) -> Result<(), CatalogError> {
414 match &self.schema {
415 Some(schema) => schema.alter_edge_type_drop_property(type_name, property_name),
416 None => Err(CatalogError::SchemaNotEnabled),
417 }
418 }
419
420 pub fn alter_graph_type_add_node_type(
422 &self,
423 graph_type_name: &str,
424 node_type: String,
425 ) -> Result<(), CatalogError> {
426 match &self.schema {
427 Some(schema) => schema.alter_graph_type_add_node_type(graph_type_name, node_type),
428 None => Err(CatalogError::SchemaNotEnabled),
429 }
430 }
431
432 pub fn alter_graph_type_drop_node_type(
434 &self,
435 graph_type_name: &str,
436 node_type: &str,
437 ) -> Result<(), CatalogError> {
438 match &self.schema {
439 Some(schema) => schema.alter_graph_type_drop_node_type(graph_type_name, node_type),
440 None => Err(CatalogError::SchemaNotEnabled),
441 }
442 }
443
444 pub fn alter_graph_type_add_edge_type(
446 &self,
447 graph_type_name: &str,
448 edge_type: String,
449 ) -> Result<(), CatalogError> {
450 match &self.schema {
451 Some(schema) => schema.alter_graph_type_add_edge_type(graph_type_name, edge_type),
452 None => Err(CatalogError::SchemaNotEnabled),
453 }
454 }
455
456 pub fn alter_graph_type_drop_edge_type(
458 &self,
459 graph_type_name: &str,
460 edge_type: &str,
461 ) -> Result<(), CatalogError> {
462 match &self.schema {
463 Some(schema) => schema.alter_graph_type_drop_edge_type(graph_type_name, edge_type),
464 None => Err(CatalogError::SchemaNotEnabled),
465 }
466 }
467
468 pub fn bind_graph_type(
470 &self,
471 graph_name: &str,
472 graph_type: String,
473 ) -> Result<(), CatalogError> {
474 match &self.schema {
475 Some(schema) => {
476 if schema.get_graph_type(&graph_type).is_none() {
478 return Err(CatalogError::TypeNotFound(graph_type));
479 }
480 schema
481 .graph_type_bindings
482 .write()
483 .insert(graph_name.to_string(), graph_type);
484 Ok(())
485 }
486 None => Err(CatalogError::SchemaNotEnabled),
487 }
488 }
489
490 pub fn get_graph_type_binding(&self, graph_name: &str) -> Option<String> {
492 self.schema
493 .as_ref()?
494 .graph_type_bindings
495 .read()
496 .get(graph_name)
497 .cloned()
498 }
499
500 pub fn register_procedure(&self, def: ProcedureDefinition) -> Result<(), CatalogError> {
502 match &self.schema {
503 Some(schema) => schema.register_procedure(def),
504 None => Err(CatalogError::SchemaNotEnabled),
505 }
506 }
507
508 pub fn replace_procedure(&self, def: ProcedureDefinition) -> Result<(), CatalogError> {
510 match &self.schema {
511 Some(schema) => {
512 schema.replace_procedure(def);
513 Ok(())
514 }
515 None => Err(CatalogError::SchemaNotEnabled),
516 }
517 }
518
519 pub fn drop_procedure(&self, name: &str) -> Result<(), CatalogError> {
521 match &self.schema {
522 Some(schema) => schema.drop_procedure(name),
523 None => Err(CatalogError::SchemaNotEnabled),
524 }
525 }
526
527 pub fn get_procedure(&self, name: &str) -> Option<ProcedureDefinition> {
529 self.schema.as_ref()?.get_procedure(name)
530 }
531}
532
533impl Default for Catalog {
534 fn default() -> Self {
535 Self::new()
536 }
537}
538
539struct LabelCatalog {
543 name_to_id: RwLock<HashMap<Arc<str>, LabelId>>,
544 id_to_name: RwLock<Vec<Arc<str>>>,
545 next_id: AtomicU32,
546}
547
548impl LabelCatalog {
549 fn new() -> Self {
550 Self {
551 name_to_id: RwLock::new(HashMap::new()),
552 id_to_name: RwLock::new(Vec::new()),
553 next_id: AtomicU32::new(0),
554 }
555 }
556
557 fn get_or_create(&self, name: &str) -> LabelId {
558 {
560 let name_to_id = self.name_to_id.read();
561 if let Some(&id) = name_to_id.get(name) {
562 return id;
563 }
564 }
565
566 let mut name_to_id = self.name_to_id.write();
568 let mut id_to_name = self.id_to_name.write();
569
570 if let Some(&id) = name_to_id.get(name) {
572 return id;
573 }
574
575 let id = LabelId::new(self.next_id.fetch_add(1, Ordering::Relaxed));
576 let name: Arc<str> = name.into();
577 name_to_id.insert(Arc::clone(&name), id);
578 id_to_name.push(name);
579 id
580 }
581
582 fn get_id(&self, name: &str) -> Option<LabelId> {
583 self.name_to_id.read().get(name).copied()
584 }
585
586 fn get_name(&self, id: LabelId) -> Option<Arc<str>> {
587 self.id_to_name.read().get(id.as_u32() as usize).cloned()
588 }
589
590 fn count(&self) -> usize {
591 self.id_to_name.read().len()
592 }
593
594 fn all_names(&self) -> Vec<Arc<str>> {
595 self.id_to_name.read().clone()
596 }
597}
598
599struct PropertyCatalog {
603 name_to_id: RwLock<HashMap<Arc<str>, PropertyKeyId>>,
604 id_to_name: RwLock<Vec<Arc<str>>>,
605 next_id: AtomicU32,
606}
607
608impl PropertyCatalog {
609 fn new() -> Self {
610 Self {
611 name_to_id: RwLock::new(HashMap::new()),
612 id_to_name: RwLock::new(Vec::new()),
613 next_id: AtomicU32::new(0),
614 }
615 }
616
617 fn get_or_create(&self, name: &str) -> PropertyKeyId {
618 {
620 let name_to_id = self.name_to_id.read();
621 if let Some(&id) = name_to_id.get(name) {
622 return id;
623 }
624 }
625
626 let mut name_to_id = self.name_to_id.write();
628 let mut id_to_name = self.id_to_name.write();
629
630 if let Some(&id) = name_to_id.get(name) {
632 return id;
633 }
634
635 let id = PropertyKeyId::new(self.next_id.fetch_add(1, Ordering::Relaxed));
636 let name: Arc<str> = name.into();
637 name_to_id.insert(Arc::clone(&name), id);
638 id_to_name.push(name);
639 id
640 }
641
642 fn get_id(&self, name: &str) -> Option<PropertyKeyId> {
643 self.name_to_id.read().get(name).copied()
644 }
645
646 fn get_name(&self, id: PropertyKeyId) -> Option<Arc<str>> {
647 self.id_to_name.read().get(id.as_u32() as usize).cloned()
648 }
649
650 fn count(&self) -> usize {
651 self.id_to_name.read().len()
652 }
653
654 fn all_names(&self) -> Vec<Arc<str>> {
655 self.id_to_name.read().clone()
656 }
657}
658
659struct EdgeTypeCatalog {
663 name_to_id: RwLock<HashMap<Arc<str>, EdgeTypeId>>,
664 id_to_name: RwLock<Vec<Arc<str>>>,
665 next_id: AtomicU32,
666}
667
668impl EdgeTypeCatalog {
669 fn new() -> Self {
670 Self {
671 name_to_id: RwLock::new(HashMap::new()),
672 id_to_name: RwLock::new(Vec::new()),
673 next_id: AtomicU32::new(0),
674 }
675 }
676
677 fn get_or_create(&self, name: &str) -> EdgeTypeId {
678 {
680 let name_to_id = self.name_to_id.read();
681 if let Some(&id) = name_to_id.get(name) {
682 return id;
683 }
684 }
685
686 let mut name_to_id = self.name_to_id.write();
688 let mut id_to_name = self.id_to_name.write();
689
690 if let Some(&id) = name_to_id.get(name) {
692 return id;
693 }
694
695 let id = EdgeTypeId::new(self.next_id.fetch_add(1, Ordering::Relaxed));
696 let name: Arc<str> = name.into();
697 name_to_id.insert(Arc::clone(&name), id);
698 id_to_name.push(name);
699 id
700 }
701
702 fn get_id(&self, name: &str) -> Option<EdgeTypeId> {
703 self.name_to_id.read().get(name).copied()
704 }
705
706 fn get_name(&self, id: EdgeTypeId) -> Option<Arc<str>> {
707 self.id_to_name.read().get(id.as_u32() as usize).cloned()
708 }
709
710 fn count(&self) -> usize {
711 self.id_to_name.read().len()
712 }
713
714 fn all_names(&self) -> Vec<Arc<str>> {
715 self.id_to_name.read().clone()
716 }
717}
718
719#[derive(Debug, Clone, Copy, PartialEq, Eq)]
723pub enum IndexType {
724 Hash,
726 BTree,
728 FullText,
730}
731
732#[derive(Debug, Clone)]
734pub struct IndexDefinition {
735 pub id: IndexId,
737 pub label: LabelId,
739 pub property_key: PropertyKeyId,
741 pub index_type: IndexType,
743}
744
745struct IndexCatalog {
747 indexes: RwLock<HashMap<IndexId, IndexDefinition>>,
748 label_indexes: RwLock<HashMap<LabelId, Vec<IndexId>>>,
749 label_property_indexes: RwLock<HashMap<(LabelId, PropertyKeyId), Vec<IndexId>>>,
750 next_id: AtomicU32,
751}
752
753impl IndexCatalog {
754 fn new() -> Self {
755 Self {
756 indexes: RwLock::new(HashMap::new()),
757 label_indexes: RwLock::new(HashMap::new()),
758 label_property_indexes: RwLock::new(HashMap::new()),
759 next_id: AtomicU32::new(0),
760 }
761 }
762
763 fn create(
764 &self,
765 label: LabelId,
766 property_key: PropertyKeyId,
767 index_type: IndexType,
768 ) -> IndexId {
769 let id = IndexId::new(self.next_id.fetch_add(1, Ordering::Relaxed));
770 let definition = IndexDefinition {
771 id,
772 label,
773 property_key,
774 index_type,
775 };
776
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 indexes.insert(id, definition);
782 label_indexes.entry(label).or_default().push(id);
783 label_property_indexes
784 .entry((label, property_key))
785 .or_default()
786 .push(id);
787
788 id
789 }
790
791 fn drop(&self, id: IndexId) -> bool {
792 let mut indexes = self.indexes.write();
793 let mut label_indexes = self.label_indexes.write();
794 let mut label_property_indexes = self.label_property_indexes.write();
795
796 if let Some(definition) = indexes.remove(&id) {
797 if let Some(ids) = label_indexes.get_mut(&definition.label) {
799 ids.retain(|&i| i != id);
800 }
801 if let Some(ids) =
803 label_property_indexes.get_mut(&(definition.label, definition.property_key))
804 {
805 ids.retain(|&i| i != id);
806 }
807 true
808 } else {
809 false
810 }
811 }
812
813 fn get(&self, id: IndexId) -> Option<IndexDefinition> {
814 self.indexes.read().get(&id).cloned()
815 }
816
817 fn for_label(&self, label: LabelId) -> Vec<IndexId> {
818 self.label_indexes
819 .read()
820 .get(&label)
821 .cloned()
822 .unwrap_or_default()
823 }
824
825 fn for_label_property(&self, label: LabelId, property_key: PropertyKeyId) -> Vec<IndexId> {
826 self.label_property_indexes
827 .read()
828 .get(&(label, property_key))
829 .cloned()
830 .unwrap_or_default()
831 }
832
833 fn count(&self) -> usize {
834 self.indexes.read().len()
835 }
836
837 fn all(&self) -> Vec<IndexDefinition> {
838 self.indexes.read().values().cloned().collect()
839 }
840}
841
842#[derive(Debug, Clone, PartialEq, Eq)]
846pub enum PropertyDataType {
847 String,
849 Int64,
851 Float64,
853 Bool,
855 Date,
857 Time,
859 Timestamp,
861 Duration,
863 List,
865 Map,
867 Bytes,
869 Any,
871}
872
873impl PropertyDataType {
874 #[must_use]
876 pub fn from_type_name(name: &str) -> Self {
877 match name.to_uppercase().as_str() {
878 "STRING" | "VARCHAR" | "TEXT" => Self::String,
879 "INT" | "INT64" | "INTEGER" | "BIGINT" => Self::Int64,
880 "FLOAT" | "FLOAT64" | "DOUBLE" | "REAL" => Self::Float64,
881 "BOOL" | "BOOLEAN" => Self::Bool,
882 "DATE" => Self::Date,
883 "TIME" => Self::Time,
884 "TIMESTAMP" | "DATETIME" => Self::Timestamp,
885 "DURATION" | "INTERVAL" => Self::Duration,
886 "LIST" | "ARRAY" => Self::List,
887 "MAP" | "RECORD" => Self::Map,
888 "BYTES" | "BINARY" | "BLOB" => Self::Bytes,
889 _ => Self::Any,
890 }
891 }
892
893 #[must_use]
895 pub fn matches(&self, value: &Value) -> bool {
896 matches!(
897 (self, value),
898 (Self::Any, _)
899 | (_, Value::Null)
900 | (Self::String, Value::String(_))
901 | (Self::Int64, Value::Int64(_))
902 | (Self::Float64, Value::Float64(_))
903 | (Self::Bool, Value::Bool(_))
904 | (Self::Date, Value::Date(_))
905 | (Self::Time, Value::Time(_))
906 | (Self::Timestamp, Value::Timestamp(_))
907 | (Self::Duration, Value::Duration(_))
908 | (Self::List, Value::List(_))
909 | (Self::Bytes, Value::Bytes(_))
910 )
911 }
912}
913
914impl std::fmt::Display for PropertyDataType {
915 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
916 match self {
917 Self::String => write!(f, "STRING"),
918 Self::Int64 => write!(f, "INT64"),
919 Self::Float64 => write!(f, "FLOAT64"),
920 Self::Bool => write!(f, "BOOLEAN"),
921 Self::Date => write!(f, "DATE"),
922 Self::Time => write!(f, "TIME"),
923 Self::Timestamp => write!(f, "TIMESTAMP"),
924 Self::Duration => write!(f, "DURATION"),
925 Self::List => write!(f, "LIST"),
926 Self::Map => write!(f, "MAP"),
927 Self::Bytes => write!(f, "BYTES"),
928 Self::Any => write!(f, "ANY"),
929 }
930 }
931}
932
933#[derive(Debug, Clone)]
935pub struct TypedProperty {
936 pub name: String,
938 pub data_type: PropertyDataType,
940 pub nullable: bool,
942 pub default_value: Option<Value>,
944}
945
946#[derive(Debug, Clone)]
948pub enum TypeConstraint {
949 PrimaryKey(Vec<String>),
951 Unique(Vec<String>),
953 NotNull(String),
955 Check {
957 name: Option<String>,
959 expression: String,
961 },
962}
963
964#[derive(Debug, Clone)]
966pub struct NodeTypeDefinition {
967 pub name: String,
969 pub properties: Vec<TypedProperty>,
971 pub constraints: Vec<TypeConstraint>,
973}
974
975#[derive(Debug, Clone)]
977pub struct EdgeTypeDefinition {
978 pub name: String,
980 pub properties: Vec<TypedProperty>,
982 pub constraints: Vec<TypeConstraint>,
984}
985
986#[derive(Debug, Clone)]
988pub struct GraphTypeDefinition {
989 pub name: String,
991 pub allowed_node_types: Vec<String>,
993 pub allowed_edge_types: Vec<String>,
995 pub open: bool,
997}
998
999#[derive(Debug, Clone)]
1001pub struct ProcedureDefinition {
1002 pub name: String,
1004 pub params: Vec<(String, String)>,
1006 pub returns: Vec<(String, String)>,
1008 pub body: String,
1010}
1011
1012pub struct SchemaCatalog {
1016 unique_constraints: RwLock<HashSet<(LabelId, PropertyKeyId)>>,
1018 required_properties: RwLock<HashSet<(LabelId, PropertyKeyId)>>,
1020 node_types: RwLock<HashMap<String, NodeTypeDefinition>>,
1022 edge_types: RwLock<HashMap<String, EdgeTypeDefinition>>,
1024 graph_types: RwLock<HashMap<String, GraphTypeDefinition>>,
1026 schemas: RwLock<Vec<String>>,
1028 graph_type_bindings: RwLock<HashMap<String, String>>,
1030 procedures: RwLock<HashMap<String, ProcedureDefinition>>,
1032}
1033
1034impl SchemaCatalog {
1035 fn new() -> Self {
1036 Self {
1037 unique_constraints: RwLock::new(HashSet::new()),
1038 required_properties: RwLock::new(HashSet::new()),
1039 node_types: RwLock::new(HashMap::new()),
1040 edge_types: RwLock::new(HashMap::new()),
1041 graph_types: RwLock::new(HashMap::new()),
1042 schemas: RwLock::new(Vec::new()),
1043 graph_type_bindings: RwLock::new(HashMap::new()),
1044 procedures: RwLock::new(HashMap::new()),
1045 }
1046 }
1047
1048 pub fn register_node_type(&self, def: NodeTypeDefinition) -> Result<(), CatalogError> {
1052 let mut types = self.node_types.write();
1053 if types.contains_key(&def.name) {
1054 return Err(CatalogError::TypeAlreadyExists(def.name));
1055 }
1056 types.insert(def.name.clone(), def);
1057 Ok(())
1058 }
1059
1060 pub fn register_or_replace_node_type(&self, def: NodeTypeDefinition) {
1062 self.node_types.write().insert(def.name.clone(), def);
1063 }
1064
1065 pub fn drop_node_type(&self, name: &str) -> Result<(), CatalogError> {
1067 let mut types = self.node_types.write();
1068 if types.remove(name).is_none() {
1069 return Err(CatalogError::TypeNotFound(name.to_string()));
1070 }
1071 Ok(())
1072 }
1073
1074 #[must_use]
1076 pub fn get_node_type(&self, name: &str) -> Option<NodeTypeDefinition> {
1077 self.node_types.read().get(name).cloned()
1078 }
1079
1080 #[must_use]
1082 pub fn all_node_types(&self) -> Vec<String> {
1083 self.node_types.read().keys().cloned().collect()
1084 }
1085
1086 pub fn register_edge_type(&self, def: EdgeTypeDefinition) -> Result<(), CatalogError> {
1090 let mut types = self.edge_types.write();
1091 if types.contains_key(&def.name) {
1092 return Err(CatalogError::TypeAlreadyExists(def.name));
1093 }
1094 types.insert(def.name.clone(), def);
1095 Ok(())
1096 }
1097
1098 pub fn register_or_replace_edge_type(&self, def: EdgeTypeDefinition) {
1100 self.edge_types.write().insert(def.name.clone(), def);
1101 }
1102
1103 pub fn drop_edge_type(&self, name: &str) -> Result<(), CatalogError> {
1105 let mut types = self.edge_types.write();
1106 if types.remove(name).is_none() {
1107 return Err(CatalogError::TypeNotFound(name.to_string()));
1108 }
1109 Ok(())
1110 }
1111
1112 #[must_use]
1114 pub fn get_edge_type(&self, name: &str) -> Option<EdgeTypeDefinition> {
1115 self.edge_types.read().get(name).cloned()
1116 }
1117
1118 #[must_use]
1120 pub fn all_edge_types(&self) -> Vec<String> {
1121 self.edge_types.read().keys().cloned().collect()
1122 }
1123
1124 pub fn register_graph_type(&self, def: GraphTypeDefinition) -> Result<(), CatalogError> {
1128 let mut types = self.graph_types.write();
1129 if types.contains_key(&def.name) {
1130 return Err(CatalogError::TypeAlreadyExists(def.name));
1131 }
1132 types.insert(def.name.clone(), def);
1133 Ok(())
1134 }
1135
1136 pub fn drop_graph_type(&self, name: &str) -> Result<(), CatalogError> {
1138 let mut types = self.graph_types.write();
1139 if types.remove(name).is_none() {
1140 return Err(CatalogError::TypeNotFound(name.to_string()));
1141 }
1142 Ok(())
1143 }
1144
1145 #[must_use]
1147 pub fn get_graph_type(&self, name: &str) -> Option<GraphTypeDefinition> {
1148 self.graph_types.read().get(name).cloned()
1149 }
1150
1151 pub fn register_schema(&self, name: String) -> Result<(), CatalogError> {
1155 let mut schemas = self.schemas.write();
1156 if schemas.contains(&name) {
1157 return Err(CatalogError::SchemaAlreadyExists(name));
1158 }
1159 schemas.push(name);
1160 Ok(())
1161 }
1162
1163 pub fn drop_schema(&self, name: &str) -> Result<(), CatalogError> {
1165 let mut schemas = self.schemas.write();
1166 if let Some(pos) = schemas.iter().position(|s| s == name) {
1167 schemas.remove(pos);
1168 Ok(())
1169 } else {
1170 Err(CatalogError::SchemaNotFound(name.to_string()))
1171 }
1172 }
1173
1174 pub fn alter_node_type_add_property(
1178 &self,
1179 type_name: &str,
1180 property: TypedProperty,
1181 ) -> Result<(), CatalogError> {
1182 let mut types = self.node_types.write();
1183 let def = types
1184 .get_mut(type_name)
1185 .ok_or_else(|| CatalogError::TypeNotFound(type_name.to_string()))?;
1186 if def.properties.iter().any(|p| p.name == property.name) {
1187 return Err(CatalogError::TypeAlreadyExists(format!(
1188 "property {} on {}",
1189 property.name, type_name
1190 )));
1191 }
1192 def.properties.push(property);
1193 Ok(())
1194 }
1195
1196 pub fn alter_node_type_drop_property(
1198 &self,
1199 type_name: &str,
1200 property_name: &str,
1201 ) -> Result<(), CatalogError> {
1202 let mut types = self.node_types.write();
1203 let def = types
1204 .get_mut(type_name)
1205 .ok_or_else(|| CatalogError::TypeNotFound(type_name.to_string()))?;
1206 let len_before = def.properties.len();
1207 def.properties.retain(|p| p.name != property_name);
1208 if def.properties.len() == len_before {
1209 return Err(CatalogError::TypeNotFound(format!(
1210 "property {} on {}",
1211 property_name, type_name
1212 )));
1213 }
1214 Ok(())
1215 }
1216
1217 pub fn alter_edge_type_add_property(
1219 &self,
1220 type_name: &str,
1221 property: TypedProperty,
1222 ) -> Result<(), CatalogError> {
1223 let mut types = self.edge_types.write();
1224 let def = types
1225 .get_mut(type_name)
1226 .ok_or_else(|| CatalogError::TypeNotFound(type_name.to_string()))?;
1227 if def.properties.iter().any(|p| p.name == property.name) {
1228 return Err(CatalogError::TypeAlreadyExists(format!(
1229 "property {} on {}",
1230 property.name, type_name
1231 )));
1232 }
1233 def.properties.push(property);
1234 Ok(())
1235 }
1236
1237 pub fn alter_edge_type_drop_property(
1239 &self,
1240 type_name: &str,
1241 property_name: &str,
1242 ) -> Result<(), CatalogError> {
1243 let mut types = self.edge_types.write();
1244 let def = types
1245 .get_mut(type_name)
1246 .ok_or_else(|| CatalogError::TypeNotFound(type_name.to_string()))?;
1247 let len_before = def.properties.len();
1248 def.properties.retain(|p| p.name != property_name);
1249 if def.properties.len() == len_before {
1250 return Err(CatalogError::TypeNotFound(format!(
1251 "property {} on {}",
1252 property_name, type_name
1253 )));
1254 }
1255 Ok(())
1256 }
1257
1258 pub fn alter_graph_type_add_node_type(
1260 &self,
1261 graph_type_name: &str,
1262 node_type: String,
1263 ) -> Result<(), CatalogError> {
1264 let mut types = self.graph_types.write();
1265 let def = types
1266 .get_mut(graph_type_name)
1267 .ok_or_else(|| CatalogError::TypeNotFound(graph_type_name.to_string()))?;
1268 if !def.allowed_node_types.contains(&node_type) {
1269 def.allowed_node_types.push(node_type);
1270 }
1271 Ok(())
1272 }
1273
1274 pub fn alter_graph_type_drop_node_type(
1276 &self,
1277 graph_type_name: &str,
1278 node_type: &str,
1279 ) -> Result<(), CatalogError> {
1280 let mut types = self.graph_types.write();
1281 let def = types
1282 .get_mut(graph_type_name)
1283 .ok_or_else(|| CatalogError::TypeNotFound(graph_type_name.to_string()))?;
1284 def.allowed_node_types.retain(|t| t != node_type);
1285 Ok(())
1286 }
1287
1288 pub fn alter_graph_type_add_edge_type(
1290 &self,
1291 graph_type_name: &str,
1292 edge_type: String,
1293 ) -> Result<(), CatalogError> {
1294 let mut types = self.graph_types.write();
1295 let def = types
1296 .get_mut(graph_type_name)
1297 .ok_or_else(|| CatalogError::TypeNotFound(graph_type_name.to_string()))?;
1298 if !def.allowed_edge_types.contains(&edge_type) {
1299 def.allowed_edge_types.push(edge_type);
1300 }
1301 Ok(())
1302 }
1303
1304 pub fn alter_graph_type_drop_edge_type(
1306 &self,
1307 graph_type_name: &str,
1308 edge_type: &str,
1309 ) -> Result<(), CatalogError> {
1310 let mut types = self.graph_types.write();
1311 let def = types
1312 .get_mut(graph_type_name)
1313 .ok_or_else(|| CatalogError::TypeNotFound(graph_type_name.to_string()))?;
1314 def.allowed_edge_types.retain(|t| t != edge_type);
1315 Ok(())
1316 }
1317
1318 pub fn register_procedure(&self, def: ProcedureDefinition) -> Result<(), CatalogError> {
1322 let mut procs = self.procedures.write();
1323 if procs.contains_key(&def.name) {
1324 return Err(CatalogError::TypeAlreadyExists(def.name.clone()));
1325 }
1326 procs.insert(def.name.clone(), def);
1327 Ok(())
1328 }
1329
1330 pub fn replace_procedure(&self, def: ProcedureDefinition) {
1332 self.procedures.write().insert(def.name.clone(), def);
1333 }
1334
1335 pub fn drop_procedure(&self, name: &str) -> Result<(), CatalogError> {
1337 let mut procs = self.procedures.write();
1338 if procs.remove(name).is_none() {
1339 return Err(CatalogError::TypeNotFound(name.to_string()));
1340 }
1341 Ok(())
1342 }
1343
1344 pub fn get_procedure(&self, name: &str) -> Option<ProcedureDefinition> {
1346 self.procedures.read().get(name).cloned()
1347 }
1348
1349 fn add_unique_constraint(
1350 &self,
1351 label: LabelId,
1352 property_key: PropertyKeyId,
1353 ) -> Result<(), CatalogError> {
1354 let mut constraints = self.unique_constraints.write();
1355 let key = (label, property_key);
1356 if !constraints.insert(key) {
1357 return Err(CatalogError::ConstraintAlreadyExists);
1358 }
1359 Ok(())
1360 }
1361
1362 fn add_required_property(
1363 &self,
1364 label: LabelId,
1365 property_key: PropertyKeyId,
1366 ) -> Result<(), CatalogError> {
1367 let mut required = self.required_properties.write();
1368 let key = (label, property_key);
1369 if !required.insert(key) {
1370 return Err(CatalogError::ConstraintAlreadyExists);
1371 }
1372 Ok(())
1373 }
1374
1375 fn is_property_required(&self, label: LabelId, property_key: PropertyKeyId) -> bool {
1376 self.required_properties
1377 .read()
1378 .contains(&(label, property_key))
1379 }
1380
1381 fn is_property_unique(&self, label: LabelId, property_key: PropertyKeyId) -> bool {
1382 self.unique_constraints
1383 .read()
1384 .contains(&(label, property_key))
1385 }
1386}
1387
1388#[derive(Debug, Clone, PartialEq, Eq)]
1392pub enum CatalogError {
1393 SchemaNotEnabled,
1395 ConstraintAlreadyExists,
1397 LabelNotFound(String),
1399 PropertyKeyNotFound(String),
1401 EdgeTypeNotFound(String),
1403 IndexNotFound(IndexId),
1405 TypeAlreadyExists(String),
1407 TypeNotFound(String),
1409 SchemaAlreadyExists(String),
1411 SchemaNotFound(String),
1413}
1414
1415impl std::fmt::Display for CatalogError {
1416 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1417 match self {
1418 Self::SchemaNotEnabled => write!(f, "Schema constraints are not enabled"),
1419 Self::ConstraintAlreadyExists => write!(f, "Constraint already exists"),
1420 Self::LabelNotFound(name) => write!(f, "Label not found: {name}"),
1421 Self::PropertyKeyNotFound(name) => write!(f, "Property key not found: {name}"),
1422 Self::EdgeTypeNotFound(name) => write!(f, "Edge type not found: {name}"),
1423 Self::IndexNotFound(id) => write!(f, "Index not found: {id}"),
1424 Self::TypeAlreadyExists(name) => write!(f, "Type already exists: {name}"),
1425 Self::TypeNotFound(name) => write!(f, "Type not found: {name}"),
1426 Self::SchemaAlreadyExists(name) => write!(f, "Schema already exists: {name}"),
1427 Self::SchemaNotFound(name) => write!(f, "Schema not found: {name}"),
1428 }
1429 }
1430}
1431
1432impl std::error::Error for CatalogError {}
1433
1434use grafeo_core::execution::operators::ConstraintValidator;
1437use grafeo_core::execution::operators::OperatorError;
1438
1439pub struct CatalogConstraintValidator {
1444 catalog: Arc<Catalog>,
1445}
1446
1447impl CatalogConstraintValidator {
1448 pub fn new(catalog: Arc<Catalog>) -> Self {
1450 Self { catalog }
1451 }
1452}
1453
1454impl ConstraintValidator for CatalogConstraintValidator {
1455 fn validate_node_property(
1456 &self,
1457 labels: &[String],
1458 key: &str,
1459 value: &Value,
1460 ) -> Result<(), OperatorError> {
1461 for label in labels {
1462 if let Some(type_def) = self.catalog.get_node_type(label)
1463 && let Some(typed_prop) = type_def.properties.iter().find(|p| p.name == key)
1464 {
1465 if !typed_prop.nullable && *value == Value::Null {
1467 return Err(OperatorError::ConstraintViolation(format!(
1468 "property '{key}' on :{label} is NOT NULL, cannot set to null"
1469 )));
1470 }
1471 if *value != Value::Null && !typed_prop.data_type.matches(value) {
1473 return Err(OperatorError::ConstraintViolation(format!(
1474 "property '{key}' on :{label} expects {:?}, got {:?}",
1475 typed_prop.data_type, value
1476 )));
1477 }
1478 }
1479 }
1480 Ok(())
1481 }
1482
1483 fn validate_node_complete(
1484 &self,
1485 labels: &[String],
1486 properties: &[(String, Value)],
1487 ) -> Result<(), OperatorError> {
1488 let prop_names: std::collections::HashSet<&str> =
1489 properties.iter().map(|(n, _)| n.as_str()).collect();
1490
1491 for label in labels {
1492 if let Some(type_def) = self.catalog.get_node_type(label) {
1493 for typed_prop in &type_def.properties {
1495 if !typed_prop.nullable
1496 && typed_prop.default_value.is_none()
1497 && !prop_names.contains(typed_prop.name.as_str())
1498 {
1499 return Err(OperatorError::ConstraintViolation(format!(
1500 "missing required property '{}' on :{label}",
1501 typed_prop.name
1502 )));
1503 }
1504 }
1505 for constraint in &type_def.constraints {
1507 match constraint {
1508 TypeConstraint::NotNull(prop_name) => {
1509 if !prop_names.contains(prop_name.as_str()) {
1510 return Err(OperatorError::ConstraintViolation(format!(
1511 "missing required property '{prop_name}' on :{label} (NOT NULL constraint)"
1512 )));
1513 }
1514 }
1515 TypeConstraint::PrimaryKey(key_props) => {
1516 for pk in key_props {
1517 if !prop_names.contains(pk.as_str()) {
1518 return Err(OperatorError::ConstraintViolation(format!(
1519 "missing primary key property '{pk}' on :{label}"
1520 )));
1521 }
1522 }
1523 }
1524 _ => {}
1525 }
1526 }
1527 }
1528 }
1529 Ok(())
1530 }
1531
1532 fn check_unique_node_property(
1533 &self,
1534 labels: &[String],
1535 key: &str,
1536 value: &Value,
1537 ) -> Result<(), OperatorError> {
1538 if *value == Value::Null {
1540 return Ok(());
1541 }
1542 for label in labels {
1543 if let Some(type_def) = self.catalog.get_node_type(label) {
1544 for constraint in &type_def.constraints {
1545 let is_unique = match constraint {
1546 TypeConstraint::Unique(props) => props.iter().any(|p| p == key),
1547 TypeConstraint::PrimaryKey(props) => props.iter().any(|p| p == key),
1548 _ => false,
1549 };
1550 if is_unique {
1551 let label_id = self.catalog.get_or_create_label(label);
1553 let prop_id = self.catalog.get_or_create_property_key(key);
1554 if self.catalog.is_property_unique(label_id, prop_id) {
1555 }
1561 }
1562 }
1563 }
1564 }
1565 Ok(())
1566 }
1567
1568 fn validate_edge_property(
1569 &self,
1570 edge_type: &str,
1571 key: &str,
1572 value: &Value,
1573 ) -> Result<(), OperatorError> {
1574 if let Some(type_def) = self.catalog.get_edge_type_def(edge_type)
1575 && let Some(typed_prop) = type_def.properties.iter().find(|p| p.name == key)
1576 {
1577 if !typed_prop.nullable && *value == Value::Null {
1579 return Err(OperatorError::ConstraintViolation(format!(
1580 "property '{key}' on :{edge_type} is NOT NULL, cannot set to null"
1581 )));
1582 }
1583 if *value != Value::Null && !typed_prop.data_type.matches(value) {
1585 return Err(OperatorError::ConstraintViolation(format!(
1586 "property '{key}' on :{edge_type} expects {:?}, got {:?}",
1587 typed_prop.data_type, value
1588 )));
1589 }
1590 }
1591 Ok(())
1592 }
1593
1594 fn validate_edge_complete(
1595 &self,
1596 edge_type: &str,
1597 properties: &[(String, Value)],
1598 ) -> Result<(), OperatorError> {
1599 if let Some(type_def) = self.catalog.get_edge_type_def(edge_type) {
1600 let prop_names: std::collections::HashSet<&str> =
1601 properties.iter().map(|(n, _)| n.as_str()).collect();
1602
1603 for typed_prop in &type_def.properties {
1604 if !typed_prop.nullable
1605 && typed_prop.default_value.is_none()
1606 && !prop_names.contains(typed_prop.name.as_str())
1607 {
1608 return Err(OperatorError::ConstraintViolation(format!(
1609 "missing required property '{}' on :{edge_type}",
1610 typed_prop.name
1611 )));
1612 }
1613 }
1614 }
1615 Ok(())
1616 }
1617}
1618
1619#[cfg(test)]
1620mod tests {
1621 use super::*;
1622 use std::thread;
1623
1624 #[test]
1625 fn test_catalog_labels() {
1626 let catalog = Catalog::new();
1627
1628 let person_id = catalog.get_or_create_label("Person");
1630 let company_id = catalog.get_or_create_label("Company");
1631
1632 assert_ne!(person_id, company_id);
1634
1635 assert_eq!(catalog.get_or_create_label("Person"), person_id);
1637
1638 assert_eq!(catalog.get_label_id("Person"), Some(person_id));
1640 assert_eq!(catalog.get_label_id("Company"), Some(company_id));
1641 assert_eq!(catalog.get_label_id("Unknown"), None);
1642
1643 assert_eq!(catalog.get_label_name(person_id).as_deref(), Some("Person"));
1645 assert_eq!(
1646 catalog.get_label_name(company_id).as_deref(),
1647 Some("Company")
1648 );
1649
1650 assert_eq!(catalog.label_count(), 2);
1652 }
1653
1654 #[test]
1655 fn test_catalog_property_keys() {
1656 let catalog = Catalog::new();
1657
1658 let name_id = catalog.get_or_create_property_key("name");
1659 let age_id = catalog.get_or_create_property_key("age");
1660
1661 assert_ne!(name_id, age_id);
1662 assert_eq!(catalog.get_or_create_property_key("name"), name_id);
1663 assert_eq!(catalog.get_property_key_id("name"), Some(name_id));
1664 assert_eq!(
1665 catalog.get_property_key_name(name_id).as_deref(),
1666 Some("name")
1667 );
1668 assert_eq!(catalog.property_key_count(), 2);
1669 }
1670
1671 #[test]
1672 fn test_catalog_edge_types() {
1673 let catalog = Catalog::new();
1674
1675 let knows_id = catalog.get_or_create_edge_type("KNOWS");
1676 let works_at_id = catalog.get_or_create_edge_type("WORKS_AT");
1677
1678 assert_ne!(knows_id, works_at_id);
1679 assert_eq!(catalog.get_or_create_edge_type("KNOWS"), knows_id);
1680 assert_eq!(catalog.get_edge_type_id("KNOWS"), Some(knows_id));
1681 assert_eq!(
1682 catalog.get_edge_type_name(knows_id).as_deref(),
1683 Some("KNOWS")
1684 );
1685 assert_eq!(catalog.edge_type_count(), 2);
1686 }
1687
1688 #[test]
1689 fn test_catalog_indexes() {
1690 let catalog = Catalog::new();
1691
1692 let person_id = catalog.get_or_create_label("Person");
1693 let name_id = catalog.get_or_create_property_key("name");
1694 let age_id = catalog.get_or_create_property_key("age");
1695
1696 let idx1 = catalog.create_index(person_id, name_id, IndexType::Hash);
1698 let idx2 = catalog.create_index(person_id, age_id, IndexType::BTree);
1699
1700 assert_ne!(idx1, idx2);
1701 assert_eq!(catalog.index_count(), 2);
1702
1703 let label_indexes = catalog.indexes_for_label(person_id);
1705 assert_eq!(label_indexes.len(), 2);
1706 assert!(label_indexes.contains(&idx1));
1707 assert!(label_indexes.contains(&idx2));
1708
1709 let name_indexes = catalog.indexes_for_label_property(person_id, name_id);
1711 assert_eq!(name_indexes.len(), 1);
1712 assert_eq!(name_indexes[0], idx1);
1713
1714 let def = catalog.get_index(idx1).unwrap();
1716 assert_eq!(def.label, person_id);
1717 assert_eq!(def.property_key, name_id);
1718 assert_eq!(def.index_type, IndexType::Hash);
1719
1720 assert!(catalog.drop_index(idx1));
1722 assert_eq!(catalog.index_count(), 1);
1723 assert!(catalog.get_index(idx1).is_none());
1724 assert_eq!(catalog.indexes_for_label(person_id).len(), 1);
1725 }
1726
1727 #[test]
1728 fn test_catalog_schema_constraints() {
1729 let catalog = Catalog::with_schema();
1730
1731 let person_id = catalog.get_or_create_label("Person");
1732 let email_id = catalog.get_or_create_property_key("email");
1733 let name_id = catalog.get_or_create_property_key("name");
1734
1735 assert!(catalog.add_unique_constraint(person_id, email_id).is_ok());
1737 assert!(catalog.add_required_property(person_id, name_id).is_ok());
1738
1739 assert!(catalog.is_property_unique(person_id, email_id));
1741 assert!(!catalog.is_property_unique(person_id, name_id));
1742 assert!(catalog.is_property_required(person_id, name_id));
1743 assert!(!catalog.is_property_required(person_id, email_id));
1744
1745 assert_eq!(
1747 catalog.add_unique_constraint(person_id, email_id),
1748 Err(CatalogError::ConstraintAlreadyExists)
1749 );
1750 }
1751
1752 #[test]
1753 fn test_catalog_schema_always_enabled() {
1754 let catalog = Catalog::new();
1756 assert!(catalog.has_schema());
1757
1758 let person_id = catalog.get_or_create_label("Person");
1759 let email_id = catalog.get_or_create_property_key("email");
1760
1761 assert_eq!(catalog.add_unique_constraint(person_id, email_id), Ok(()));
1763 }
1764
1765 #[test]
1768 fn test_catalog_default() {
1769 let catalog = Catalog::default();
1770 assert!(catalog.has_schema());
1771 assert_eq!(catalog.label_count(), 0);
1772 assert_eq!(catalog.property_key_count(), 0);
1773 assert_eq!(catalog.edge_type_count(), 0);
1774 assert_eq!(catalog.index_count(), 0);
1775 }
1776
1777 #[test]
1778 fn test_catalog_all_labels() {
1779 let catalog = Catalog::new();
1780
1781 catalog.get_or_create_label("Person");
1782 catalog.get_or_create_label("Company");
1783 catalog.get_or_create_label("Product");
1784
1785 let all = catalog.all_labels();
1786 assert_eq!(all.len(), 3);
1787 assert!(all.iter().any(|l| l.as_ref() == "Person"));
1788 assert!(all.iter().any(|l| l.as_ref() == "Company"));
1789 assert!(all.iter().any(|l| l.as_ref() == "Product"));
1790 }
1791
1792 #[test]
1793 fn test_catalog_all_property_keys() {
1794 let catalog = Catalog::new();
1795
1796 catalog.get_or_create_property_key("name");
1797 catalog.get_or_create_property_key("age");
1798 catalog.get_or_create_property_key("email");
1799
1800 let all = catalog.all_property_keys();
1801 assert_eq!(all.len(), 3);
1802 assert!(all.iter().any(|k| k.as_ref() == "name"));
1803 assert!(all.iter().any(|k| k.as_ref() == "age"));
1804 assert!(all.iter().any(|k| k.as_ref() == "email"));
1805 }
1806
1807 #[test]
1808 fn test_catalog_all_edge_types() {
1809 let catalog = Catalog::new();
1810
1811 catalog.get_or_create_edge_type("KNOWS");
1812 catalog.get_or_create_edge_type("WORKS_AT");
1813 catalog.get_or_create_edge_type("LIVES_IN");
1814
1815 let all = catalog.all_edge_types();
1816 assert_eq!(all.len(), 3);
1817 assert!(all.iter().any(|t| t.as_ref() == "KNOWS"));
1818 assert!(all.iter().any(|t| t.as_ref() == "WORKS_AT"));
1819 assert!(all.iter().any(|t| t.as_ref() == "LIVES_IN"));
1820 }
1821
1822 #[test]
1823 fn test_catalog_invalid_id_lookup() {
1824 let catalog = Catalog::new();
1825
1826 let _ = catalog.get_or_create_label("Person");
1828
1829 let invalid_label = LabelId::new(999);
1831 let invalid_property = PropertyKeyId::new(999);
1832 let invalid_edge_type = EdgeTypeId::new(999);
1833 let invalid_index = IndexId::new(999);
1834
1835 assert!(catalog.get_label_name(invalid_label).is_none());
1836 assert!(catalog.get_property_key_name(invalid_property).is_none());
1837 assert!(catalog.get_edge_type_name(invalid_edge_type).is_none());
1838 assert!(catalog.get_index(invalid_index).is_none());
1839 }
1840
1841 #[test]
1842 fn test_catalog_drop_nonexistent_index() {
1843 let catalog = Catalog::new();
1844 let invalid_index = IndexId::new(999);
1845 assert!(!catalog.drop_index(invalid_index));
1846 }
1847
1848 #[test]
1849 fn test_catalog_indexes_for_nonexistent_label() {
1850 let catalog = Catalog::new();
1851 let invalid_label = LabelId::new(999);
1852 let invalid_property = PropertyKeyId::new(999);
1853
1854 assert!(catalog.indexes_for_label(invalid_label).is_empty());
1855 assert!(
1856 catalog
1857 .indexes_for_label_property(invalid_label, invalid_property)
1858 .is_empty()
1859 );
1860 }
1861
1862 #[test]
1863 fn test_catalog_multiple_indexes_same_property() {
1864 let catalog = Catalog::new();
1865
1866 let person_id = catalog.get_or_create_label("Person");
1867 let name_id = catalog.get_or_create_property_key("name");
1868
1869 let hash_idx = catalog.create_index(person_id, name_id, IndexType::Hash);
1871 let btree_idx = catalog.create_index(person_id, name_id, IndexType::BTree);
1872 let fulltext_idx = catalog.create_index(person_id, name_id, IndexType::FullText);
1873
1874 assert_eq!(catalog.index_count(), 3);
1875
1876 let indexes = catalog.indexes_for_label_property(person_id, name_id);
1877 assert_eq!(indexes.len(), 3);
1878 assert!(indexes.contains(&hash_idx));
1879 assert!(indexes.contains(&btree_idx));
1880 assert!(indexes.contains(&fulltext_idx));
1881
1882 assert_eq!(
1884 catalog.get_index(hash_idx).unwrap().index_type,
1885 IndexType::Hash
1886 );
1887 assert_eq!(
1888 catalog.get_index(btree_idx).unwrap().index_type,
1889 IndexType::BTree
1890 );
1891 assert_eq!(
1892 catalog.get_index(fulltext_idx).unwrap().index_type,
1893 IndexType::FullText
1894 );
1895 }
1896
1897 #[test]
1898 fn test_catalog_schema_required_property_duplicate() {
1899 let catalog = Catalog::with_schema();
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.add_required_property(person_id, name_id).is_ok());
1906
1907 assert_eq!(
1909 catalog.add_required_property(person_id, name_id),
1910 Err(CatalogError::ConstraintAlreadyExists)
1911 );
1912 }
1913
1914 #[test]
1915 fn test_catalog_schema_check_without_constraints() {
1916 let catalog = Catalog::new();
1917
1918 let person_id = catalog.get_or_create_label("Person");
1919 let name_id = catalog.get_or_create_property_key("name");
1920
1921 assert!(!catalog.is_property_unique(person_id, name_id));
1923 assert!(!catalog.is_property_required(person_id, name_id));
1924 }
1925
1926 #[test]
1927 fn test_catalog_has_schema() {
1928 let catalog = Catalog::new();
1930 assert!(catalog.has_schema());
1931
1932 let with_schema = Catalog::with_schema();
1933 assert!(with_schema.has_schema());
1934 }
1935
1936 #[test]
1937 fn test_catalog_error_display() {
1938 assert_eq!(
1939 CatalogError::SchemaNotEnabled.to_string(),
1940 "Schema constraints are not enabled"
1941 );
1942 assert_eq!(
1943 CatalogError::ConstraintAlreadyExists.to_string(),
1944 "Constraint already exists"
1945 );
1946 assert_eq!(
1947 CatalogError::LabelNotFound("Person".to_string()).to_string(),
1948 "Label not found: Person"
1949 );
1950 assert_eq!(
1951 CatalogError::PropertyKeyNotFound("name".to_string()).to_string(),
1952 "Property key not found: name"
1953 );
1954 assert_eq!(
1955 CatalogError::EdgeTypeNotFound("KNOWS".to_string()).to_string(),
1956 "Edge type not found: KNOWS"
1957 );
1958 let idx = IndexId::new(42);
1959 assert!(CatalogError::IndexNotFound(idx).to_string().contains("42"));
1960 }
1961
1962 #[test]
1963 fn test_catalog_concurrent_label_creation() {
1964 use std::sync::Arc;
1965
1966 let catalog = Arc::new(Catalog::new());
1967 let mut handles = vec![];
1968
1969 for i in 0..10 {
1971 let catalog = Arc::clone(&catalog);
1972 handles.push(thread::spawn(move || {
1973 let label_name = format!("Label{}", i % 3); catalog.get_or_create_label(&label_name)
1975 }));
1976 }
1977
1978 let mut ids: Vec<LabelId> = handles.into_iter().map(|h| h.join().unwrap()).collect();
1979 ids.sort_by_key(|id| id.as_u32());
1980 ids.dedup();
1981
1982 assert_eq!(ids.len(), 3);
1984 assert_eq!(catalog.label_count(), 3);
1985 }
1986
1987 #[test]
1988 fn test_catalog_concurrent_property_key_creation() {
1989 use std::sync::Arc;
1990
1991 let catalog = Arc::new(Catalog::new());
1992 let mut handles = vec![];
1993
1994 for i in 0..10 {
1995 let catalog = Arc::clone(&catalog);
1996 handles.push(thread::spawn(move || {
1997 let key_name = format!("key{}", i % 4);
1998 catalog.get_or_create_property_key(&key_name)
1999 }));
2000 }
2001
2002 let mut ids: Vec<PropertyKeyId> = handles.into_iter().map(|h| h.join().unwrap()).collect();
2003 ids.sort_by_key(|id| id.as_u32());
2004 ids.dedup();
2005
2006 assert_eq!(ids.len(), 4);
2007 assert_eq!(catalog.property_key_count(), 4);
2008 }
2009
2010 #[test]
2011 fn test_catalog_concurrent_index_operations() {
2012 use std::sync::Arc;
2013
2014 let catalog = Arc::new(Catalog::new());
2015 let label = catalog.get_or_create_label("Node");
2016
2017 let mut handles = vec![];
2018
2019 for i in 0..5 {
2021 let catalog = Arc::clone(&catalog);
2022 handles.push(thread::spawn(move || {
2023 let prop = PropertyKeyId::new(i);
2024 catalog.create_index(label, prop, IndexType::Hash)
2025 }));
2026 }
2027
2028 let ids: Vec<IndexId> = handles.into_iter().map(|h| h.join().unwrap()).collect();
2029 assert_eq!(ids.len(), 5);
2030 assert_eq!(catalog.index_count(), 5);
2031 }
2032
2033 #[test]
2034 fn test_catalog_special_characters_in_names() {
2035 let catalog = Catalog::new();
2036
2037 let label1 = catalog.get_or_create_label("Label With Spaces");
2039 let label2 = catalog.get_or_create_label("Label-With-Dashes");
2040 let label3 = catalog.get_or_create_label("Label_With_Underscores");
2041 let label4 = catalog.get_or_create_label("LabelWithUnicode\u{00E9}");
2042
2043 assert_ne!(label1, label2);
2044 assert_ne!(label2, label3);
2045 assert_ne!(label3, label4);
2046
2047 assert_eq!(
2048 catalog.get_label_name(label1).as_deref(),
2049 Some("Label With Spaces")
2050 );
2051 assert_eq!(
2052 catalog.get_label_name(label4).as_deref(),
2053 Some("LabelWithUnicode\u{00E9}")
2054 );
2055 }
2056
2057 #[test]
2058 fn test_catalog_empty_names() {
2059 let catalog = Catalog::new();
2060
2061 let empty_label = catalog.get_or_create_label("");
2063 let empty_prop = catalog.get_or_create_property_key("");
2064 let empty_edge = catalog.get_or_create_edge_type("");
2065
2066 assert_eq!(catalog.get_label_name(empty_label).as_deref(), Some(""));
2067 assert_eq!(
2068 catalog.get_property_key_name(empty_prop).as_deref(),
2069 Some("")
2070 );
2071 assert_eq!(catalog.get_edge_type_name(empty_edge).as_deref(), Some(""));
2072
2073 assert_eq!(catalog.get_or_create_label(""), empty_label);
2075 }
2076
2077 #[test]
2078 fn test_catalog_large_number_of_entries() {
2079 let catalog = Catalog::new();
2080
2081 for i in 0..1000 {
2083 catalog.get_or_create_label(&format!("Label{}", i));
2084 }
2085
2086 assert_eq!(catalog.label_count(), 1000);
2087
2088 let all = catalog.all_labels();
2090 assert_eq!(all.len(), 1000);
2091
2092 let id = catalog.get_label_id("Label500").unwrap();
2094 assert_eq!(catalog.get_label_name(id).as_deref(), Some("Label500"));
2095 }
2096
2097 #[test]
2098 fn test_index_definition_debug() {
2099 let def = IndexDefinition {
2100 id: IndexId::new(1),
2101 label: LabelId::new(2),
2102 property_key: PropertyKeyId::new(3),
2103 index_type: IndexType::Hash,
2104 };
2105
2106 let debug_str = format!("{:?}", def);
2108 assert!(debug_str.contains("IndexDefinition"));
2109 assert!(debug_str.contains("Hash"));
2110 }
2111
2112 #[test]
2113 fn test_index_type_equality() {
2114 assert_eq!(IndexType::Hash, IndexType::Hash);
2115 assert_ne!(IndexType::Hash, IndexType::BTree);
2116 assert_ne!(IndexType::BTree, IndexType::FullText);
2117
2118 let t = IndexType::Hash;
2120 let t2 = t;
2121 assert_eq!(t, t2);
2122 }
2123
2124 #[test]
2125 fn test_catalog_error_equality() {
2126 assert_eq!(
2127 CatalogError::SchemaNotEnabled,
2128 CatalogError::SchemaNotEnabled
2129 );
2130 assert_eq!(
2131 CatalogError::ConstraintAlreadyExists,
2132 CatalogError::ConstraintAlreadyExists
2133 );
2134 assert_eq!(
2135 CatalogError::LabelNotFound("X".to_string()),
2136 CatalogError::LabelNotFound("X".to_string())
2137 );
2138 assert_ne!(
2139 CatalogError::LabelNotFound("X".to_string()),
2140 CatalogError::LabelNotFound("Y".to_string())
2141 );
2142 }
2143}