Skip to main content

linkrs_core/metadata/
schema_manager.rs

1use super::schema_events::{SchemaChangeCallback, SchemaChangeEvent};
2use crate::event_dispatch::{EventFilter, EventSubscriptions, SubscriptionId};
3use crate::metadata::sequence::SequenceDef;
4use crate::types::{EdgeTypeInfo, PropertyDef, SpaceInfo, TagInfo};
5use crate::StorageError;
6use dashmap::DashMap;
7use parking_lot::RwLock;
8use std::collections::HashMap;
9use std::path::Path;
10use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
11use std::sync::Arc;
12
13const SCHEMA_FORMAT_VERSION: u32 = 1;
14
15#[derive(serde::Serialize, serde::Deserialize)]
16struct SchemaSnapshot {
17    version: u32,
18    spaces: Vec<SpaceInfo>,
19    tags: Vec<(u64, TagInfo)>,
20    edge_types: Vec<(u64, EdgeTypeInfo)>,
21    space_id_counter: u64,
22    tag_id_counters: Vec<(u64, u32)>,
23    edge_type_id_counters: Vec<(u64, u32)>,
24    /// Persisted SERIAL counters: (space_id, table name, next value).
25    #[serde(default)]
26    serial_next: Vec<(u64, String, u64)>,
27    /// Persisted sequence definitions.
28    #[serde(default)]
29    sequences: Vec<SequenceDef>,
30}
31
32#[derive(Debug, Clone)]
33struct SpaceData {
34    info: SpaceInfo,
35}
36
37#[derive(Debug, Clone)]
38struct TagData {
39    info: TagInfo,
40}
41
42#[derive(Debug, Clone)]
43struct EdgeTypeData {
44    info: EdgeTypeInfo,
45}
46
47pub struct SchemaManager {
48    spaces: Arc<RwLock<HashMap<u64, SpaceData>>>,
49    space_name_index: Arc<RwLock<HashMap<String, u64>>>,
50    tags: Arc<RwLock<HashMap<(u64, u32), TagData>>>,
51    edge_types: Arc<RwLock<HashMap<(u64, u32), EdgeTypeData>>>,
52    space_id_counter: Arc<AtomicU64>,
53    tag_id_counter: Arc<DashMap<u64, AtomicU32>>,
54    edge_type_id_counter: Arc<DashMap<u64, AtomicU32>>,
55    serial_next: Arc<RwLock<Vec<(u64, String, u64)>>>,
56    sequences: Arc<RwLock<HashMap<String, SequenceDef>>>,
57    schema_callbacks: Arc<EventSubscriptions<SchemaChangeEvent>>,
58}
59
60impl Clone for SchemaManager {
61    fn clone(&self) -> Self {
62        Self {
63            spaces: self.spaces.clone(),
64            space_name_index: self.space_name_index.clone(),
65            tags: self.tags.clone(),
66            edge_types: self.edge_types.clone(),
67            space_id_counter: self.space_id_counter.clone(),
68            tag_id_counter: self.tag_id_counter.clone(),
69            edge_type_id_counter: self.edge_type_id_counter.clone(),
70            serial_next: self.serial_next.clone(),
71            sequences: self.sequences.clone(),
72            schema_callbacks: self.schema_callbacks.clone(),
73        }
74    }
75}
76
77impl std::fmt::Debug for SchemaManager {
78    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
79        f.debug_struct("SchemaManager")
80            .field("spaces_count", &self.spaces.read().len())
81            .finish()
82    }
83}
84
85impl SchemaManager {
86    pub fn new() -> Self {
87        Self {
88            spaces: Arc::new(RwLock::new(HashMap::new())),
89            space_name_index: Arc::new(RwLock::new(HashMap::new())),
90            tags: Arc::new(RwLock::new(HashMap::new())),
91            edge_types: Arc::new(RwLock::new(HashMap::new())),
92            space_id_counter: Arc::new(AtomicU64::new(0)),
93            tag_id_counter: Arc::new(DashMap::new()),
94            edge_type_id_counter: Arc::new(DashMap::new()),
95            serial_next: Arc::new(RwLock::new(Vec::new())),
96            sequences: Arc::new(RwLock::new(HashMap::new())),
97            schema_callbacks: Arc::new(EventSubscriptions::new()),
98        }
99    }
100
101    /// Build a manager sharing one schema-event registry with `IndexManager`.
102    ///
103    /// Single enum dual emission source: table/space DDL is emitted by
104    /// `SchemaManager`, index DDL by `IndexManager`; sharing one registry
105    /// lets observers subscribe once and receive both halves.
106    pub fn with_shared_schema_callbacks(
107        shared: Arc<EventSubscriptions<SchemaChangeEvent>>,
108    ) -> Self {
109        let mut manager = Self::new();
110        manager.schema_callbacks = shared;
111        manager
112    }
113
114    /// Shared schema-event registry behind this manager.
115    pub fn shared_schema_callbacks(&self) -> Arc<EventSubscriptions<SchemaChangeEvent>> {
116        Arc::clone(&self.schema_callbacks)
117    }
118
119    /// Register a runtime observer for schema changes.
120    pub fn register_schema_callback(&self, callback: SchemaChangeCallback) -> SubscriptionId {
121        self.schema_callbacks.add(callback)
122    }
123
124    /// Register a filtered observer invoked only when `filter` returns true.
125    pub fn register_schema_callback_filtered(
126        &self,
127        callback: SchemaChangeCallback,
128        filter: EventFilter<SchemaChangeEvent>,
129    ) -> SubscriptionId {
130        self.schema_callbacks.add_filtered(callback, Some(filter))
131    }
132
133    /// Remove a previously registered observer. Returns true if present.
134    pub fn unregister_schema_callback(&self, id: SubscriptionId) -> bool {
135        self.schema_callbacks.remove(id)
136    }
137
138    /// Number of registered schema observers (useful for tests and diagnostics).
139    pub fn schema_callback_count(&self) -> usize {
140        self.schema_callbacks.len()
141    }
142
143    fn emit_schema_event(&self, event: SchemaChangeEvent) {
144        if self.schema_callbacks.is_empty() {
145            return;
146        }
147        self.schema_callbacks.dispatch("schema", &event);
148    }
149
150    /// Set the persisted SERIAL counters (space_id, table name, next value).
151    /// Called by the storage layer before the schema snapshot is saved.
152    pub fn set_serial_next(&self, entries: Vec<(u64, String, u64)>) {
153        *self.serial_next.write() = entries;
154    }
155
156    /// Persisted SERIAL counters as (space_id, table name, next value) triples.
157    pub fn serial_next(&self) -> Vec<(u64, String, u64)> {
158        self.serial_next.read().clone()
159    }
160
161    fn get_next_space_id(&self) -> u64 {
162        self.space_id_counter.fetch_add(1, Ordering::SeqCst) + 1
163    }
164
165    pub fn peek_next_space_id(&self) -> u64 {
166        self.space_id_counter.load(Ordering::SeqCst) + 1
167    }
168
169    fn get_next_tag_id(&self, _space_id: u64) -> u32 {
170        let entry = self
171            .tag_id_counter
172            .entry(0)
173            .or_insert_with(|| AtomicU32::new(0));
174        entry.fetch_add(1, Ordering::SeqCst) + 1
175    }
176
177    pub fn peek_next_tag_id(&self) -> u32 {
178        let entry = self
179            .tag_id_counter
180            .entry(0)
181            .or_insert_with(|| AtomicU32::new(0));
182        entry.load(Ordering::SeqCst) + 1
183    }
184
185    fn get_next_edge_type_id(&self, _space_id: u64) -> u32 {
186        let entry = self
187            .edge_type_id_counter
188            .entry(0)
189            .or_insert_with(|| AtomicU32::new(0));
190        entry.fetch_add(1, Ordering::SeqCst) + 1
191    }
192
193    pub fn peek_next_edge_type_id(&self) -> u32 {
194        let entry = self
195            .edge_type_id_counter
196            .entry(0)
197            .or_insert_with(|| AtomicU32::new(0));
198        entry.load(Ordering::SeqCst) + 1
199    }
200
201    pub fn create_space(&self, space: &mut SpaceInfo) -> Result<bool, StorageError> {
202        let mut name_index = self.space_name_index.write();
203        if name_index.contains_key(&space.space_name) {
204            return Ok(false);
205        }
206
207        let space_id = if space.space_id == 0 {
208            self.get_next_space_id()
209        } else {
210            let current = self.space_id_counter.load(Ordering::SeqCst);
211            if space.space_id > current {
212                self.space_id_counter
213                    .store(space.space_id, Ordering::SeqCst);
214            }
215
216            let spaces = self.spaces.read();
217            if spaces.contains_key(&space.space_id) {
218                return Err(StorageError::label_already_exists(format!(
219                    "space_id {}",
220                    space.space_id
221                )));
222            }
223            space.space_id
224        };
225        space.space_id = space_id;
226
227        name_index.insert(space.space_name.clone(), space_id);
228        drop(name_index);
229
230        let mut spaces = self.spaces.write();
231        spaces.insert(
232            space_id,
233            SpaceData {
234                info: space.clone(),
235            },
236        );
237        drop(spaces);
238        let space_name = space.space_name.clone();
239        self.emit_schema_event(SchemaChangeEvent::SpaceCreated {
240            space_id,
241            space_name,
242        });
243
244        Ok(true)
245    }
246
247    pub fn drop_space(&self, space_name: &str) -> Result<bool, StorageError> {
248        let mut name_index = self.space_name_index.write();
249        if let Some(space_id) = name_index.remove(space_name) {
250            drop(name_index);
251
252            let mut spaces = self.spaces.write();
253            spaces.remove(&space_id);
254            drop(spaces);
255
256            let mut tags = self.tags.write();
257            tags.retain(|(sid, _), _| *sid != space_id);
258            drop(tags);
259
260            let mut edge_types = self.edge_types.write();
261            edge_types.retain(|(sid, _), _| *sid != space_id);
262            drop(edge_types);
263
264            self.emit_schema_event(SchemaChangeEvent::SpaceDropped {
265                space_id,
266                space_name: space_name.to_string(),
267            });
268
269            Ok(true)
270        } else {
271            Ok(false)
272        }
273    }
274
275    pub fn clear_space(&self, space_name: &str) -> Result<bool, StorageError> {
276        let space_info = self.get_space(space_name)?.ok_or_else(|| {
277            StorageError::db_error(format!("Space \"{}\" does not exist", space_name))
278        })?;
279
280        let space_id = space_info.space_id;
281
282        let mut tags = self.tags.write();
283        tags.retain(|(sid, _), _| *sid != space_id);
284        drop(tags);
285
286        let mut edge_types = self.edge_types.write();
287        edge_types.retain(|(sid, _), _| *sid != space_id);
288        drop(edge_types);
289
290        self.emit_schema_event(SchemaChangeEvent::SpaceCleared {
291            space_id,
292            space_name: space_name.to_string(),
293        });
294
295        Ok(true)
296    }
297
298    pub fn get_space(&self, space_name: &str) -> Result<Option<SpaceInfo>, StorageError> {
299        let name_index = self.space_name_index.read();
300        if let Some(space_id) = name_index.get(space_name) {
301            let spaces = self.spaces.read();
302            if let Some(data) = spaces.get(space_id) {
303                return Ok(Some(data.info.clone()));
304            }
305        }
306        Ok(None)
307    }
308
309    pub fn get_space_id(&self, space_name: &str) -> Result<u64, StorageError> {
310        let name_index = self.space_name_index.read();
311        if let Some(space_id) = name_index.get(space_name) {
312            Ok(*space_id)
313        } else {
314            Err(StorageError::db_error(format!(
315                "Space \"{}\" does not exist",
316                space_name
317            )))
318        }
319    }
320
321    pub fn get_space_by_id(&self, space_id: u64) -> Result<Option<SpaceInfo>, StorageError> {
322        let spaces = self.spaces.read();
323        Ok(spaces.get(&space_id).map(|d| d.info.clone()))
324    }
325
326    pub fn list_spaces(&self) -> Result<Vec<SpaceInfo>, StorageError> {
327        let spaces = self.spaces.read();
328        Ok(spaces.values().map(|d| d.info.clone()).collect())
329    }
330
331    pub fn update_space(&self, space: &SpaceInfo) -> Result<bool, StorageError> {
332        let applied = {
333            let mut spaces = self.spaces.write();
334            if let std::collections::hash_map::Entry::Occupied(mut e) = spaces.entry(space.space_id)
335            {
336                e.insert(SpaceData {
337                    info: space.clone(),
338                });
339                true
340            } else {
341                false
342            }
343        };
344        if applied {
345            self.emit_schema_event(SchemaChangeEvent::SpaceUpdated {
346                space_id: space.space_id,
347                space_name: space.space_name.clone(),
348            });
349        }
350        Ok(applied)
351    }
352
353    pub fn create_tag(&self, space_name: &str, tag: &TagInfo) -> Result<u32, StorageError> {
354        let space_info = self.get_space(space_name)?.ok_or_else(|| {
355            StorageError::db_error(format!("Space \"{}\" does not exist", space_name))
356        })?;
357
358        let existing_tags = self.list_tags(space_name)?;
359        if existing_tags.iter().any(|t| t.tag_name == tag.tag_name) {
360            return Err(StorageError::label_already_exists(tag.tag_name.clone()));
361        }
362
363        let tag_id = self.get_next_tag_id(space_info.space_id);
364        let mut tag_with_id = tag.clone();
365        tag_with_id.tag_id = tag_id;
366
367        {
368            let mut tags = self.tags.write();
369            tags.insert((space_info.space_id, tag_id), TagData { info: tag_with_id });
370        }
371
372        self.emit_schema_event(SchemaChangeEvent::TagCreated {
373            space_id: space_info.space_id,
374            space_name: space_name.to_string(),
375            tag_id,
376            tag_name: tag.tag_name.clone(),
377        });
378
379        Ok(tag_id)
380    }
381
382    pub fn create_tag_with_id(
383        &self,
384        space_name: &str,
385        tag: &TagInfo,
386        tag_id: u32,
387    ) -> Result<u32, StorageError> {
388        let space_info = self.get_space(space_name)?.ok_or_else(|| {
389            StorageError::db_error(format!("Space \"{}\" does not exist", space_name))
390        })?;
391
392        let existing_tags = self.list_tags(space_name)?;
393        if existing_tags
394            .iter()
395            .any(|existing| existing.tag_name == tag.tag_name)
396        {
397            return Err(StorageError::label_already_exists(tag.tag_name.clone()));
398        }
399
400        let mut tag_with_id = tag.clone();
401        tag_with_id.tag_id = tag_id;
402
403        {
404            let mut tags = self.tags.write();
405            tags.insert((space_info.space_id, tag_id), TagData { info: tag_with_id });
406        }
407
408        let entry = self
409            .tag_id_counter
410            .entry(0)
411            .or_insert_with(|| AtomicU32::new(0));
412        let current = entry.load(Ordering::SeqCst);
413        if tag_id > current {
414            entry.store(tag_id, Ordering::SeqCst);
415        }
416
417        self.emit_schema_event(SchemaChangeEvent::TagCreated {
418            space_id: space_info.space_id,
419            space_name: space_name.to_string(),
420            tag_id,
421            tag_name: tag.tag_name.clone(),
422        });
423
424        Ok(tag_id)
425    }
426
427    pub fn drop_tag(&self, space_name: &str, tag_name: &str) -> Result<bool, StorageError> {
428        let space_info = self.get_space(space_name)?.ok_or_else(|| {
429            StorageError::db_error(format!("Space \"{}\" does not exist", space_name))
430        })?;
431
432        let removed = {
433            let mut tags = self.tags.write();
434            let tag_key = tags
435                .iter()
436                .find(|((sid, _), data)| {
437                    *sid == space_info.space_id && data.info.tag_name == tag_name
438                })
439                .map(|(k, _)| *k);
440
441            if let Some(key) = tag_key {
442                let removed = tags.remove(&key);
443                removed.map(|d| d.info.tag_id)
444            } else {
445                None
446            }
447        };
448
449        if let Some(tag_id) = removed {
450            self.emit_schema_event(SchemaChangeEvent::TagDropped {
451                space_id: space_info.space_id,
452                space_name: space_name.to_string(),
453                tag_id,
454                tag_name: tag_name.to_string(),
455            });
456            Ok(true)
457        } else {
458            Ok(false)
459        }
460    }
461
462    pub fn get_tag(
463        &self,
464        space_name: &str,
465        tag_name: &str,
466    ) -> Result<Option<TagInfo>, StorageError> {
467        let space_info = self.get_space(space_name)?.ok_or_else(|| {
468            StorageError::db_error(format!("Space \"{}\" does not exist", space_name))
469        })?;
470
471        let tags = self.tags.read();
472        Ok(tags
473            .iter()
474            .find(|((sid, _), data)| *sid == space_info.space_id && data.info.tag_name == tag_name)
475            .map(|(_, data)| data)
476            .map(|d| d.info.clone()))
477    }
478
479    pub fn list_tags(&self, space_name: &str) -> Result<Vec<TagInfo>, StorageError> {
480        let space_info = self.get_space(space_name)?.ok_or_else(|| {
481            StorageError::db_error(format!("Space \"{}\" does not exist", space_name))
482        })?;
483
484        let tags = self.tags.read();
485        Ok(tags
486            .iter()
487            .filter(|((sid, _), _)| *sid == space_info.space_id)
488            .map(|(_, data)| data.info.clone())
489            .collect())
490    }
491
492    pub fn find_tag_by_id(&self, tag_id: u32) -> Option<(String, TagInfo)> {
493        let tags = self.tags.read();
494        let spaces = self.spaces.read();
495
496        tags.iter().find_map(|((space_id, current_tag_id), data)| {
497            if *current_tag_id != tag_id {
498                return None;
499            }
500
501            let space_name = spaces
502                .get(space_id)
503                .map(|space| space.info.space_name.clone())?;
504
505            Some((space_name, data.info.clone()))
506        })
507    }
508
509    pub fn update_tag(&self, space_name: &str, tag: &TagInfo) -> Result<bool, StorageError> {
510        let space_info = self.get_space(space_name)?.ok_or_else(|| {
511            StorageError::db_error(format!("Space \"{}\" does not exist", space_name))
512        })?;
513
514        let (applied, tag_id) = {
515            let mut tags = self.tags.write();
516            let tag_key = tags
517                .iter()
518                .find(|((sid, _), data)| {
519                    *sid == space_info.space_id && data.info.tag_name == tag.tag_name
520                })
521                .map(|(k, _)| *k);
522
523            if let Some(key) = tag_key {
524                if let Some(data) = tags.get_mut(&key) {
525                    let tag_id = data.info.tag_id;
526                    data.info = tag.clone();
527                    (true, tag_id)
528                } else {
529                    (false, 0)
530                }
531            } else {
532                (false, 0)
533            }
534        };
535        if applied {
536            self.emit_schema_event(SchemaChangeEvent::TagAltered {
537                space_id: space_info.space_id,
538                space_name: space_name.to_string(),
539                tag_id,
540                tag_name: tag.tag_name.clone(),
541                added_properties: tag.properties.iter().map(|p| p.name.clone()).collect(),
542                removed_properties: Vec::new(),
543            });
544        }
545        Ok(applied)
546    }
547
548    pub fn create_edge_type(
549        &self,
550        space_name: &str,
551        edge_type: &EdgeTypeInfo,
552    ) -> Result<u32, StorageError> {
553        let space_info = self.get_space(space_name)?.ok_or_else(|| {
554            StorageError::db_error(format!("Space \"{}\" does not exist", space_name))
555        })?;
556
557        let existing = self.list_edge_types(space_name)?;
558        if existing
559            .iter()
560            .any(|e| e.edge_type_name == edge_type.edge_type_name)
561        {
562            return Err(StorageError::label_already_exists(
563                edge_type.edge_type_name.clone(),
564            ));
565        }
566
567        let edge_type_id = self.get_next_edge_type_id(space_info.space_id);
568        let mut edge_with_id = edge_type.clone();
569        edge_with_id.edge_type_id = edge_type_id;
570
571        {
572            let mut edge_types = self.edge_types.write();
573            edge_types.insert(
574                (space_info.space_id, edge_type_id),
575                EdgeTypeData { info: edge_with_id },
576            );
577        }
578
579        self.emit_schema_event(SchemaChangeEvent::EdgeTypeCreated {
580            space_id: space_info.space_id,
581            space_name: space_name.to_string(),
582            edge_type_id,
583            type_name: edge_type.edge_type_name.clone(),
584        });
585
586        Ok(edge_type_id)
587    }
588
589    pub fn create_edge_type_with_id(
590        &self,
591        space_name: &str,
592        edge_type: &EdgeTypeInfo,
593        edge_type_id: u32,
594    ) -> Result<u32, StorageError> {
595        let space_info = self.get_space(space_name)?.ok_or_else(|| {
596            StorageError::db_error(format!("Space \"{}\" does not exist", space_name))
597        })?;
598
599        let existing = self.list_edge_types(space_name)?;
600        if existing
601            .iter()
602            .any(|e| e.edge_type_name == edge_type.edge_type_name)
603        {
604            return Err(StorageError::label_already_exists(
605                edge_type.edge_type_name.clone(),
606            ));
607        }
608
609        let mut edge_with_id = edge_type.clone();
610        edge_with_id.edge_type_id = edge_type_id;
611
612        {
613            let mut edge_types = self.edge_types.write();
614            edge_types.insert(
615                (space_info.space_id, edge_type_id),
616                EdgeTypeData { info: edge_with_id },
617            );
618        }
619
620        let entry = self
621            .edge_type_id_counter
622            .entry(0)
623            .or_insert_with(|| AtomicU32::new(0));
624        let current = entry.load(Ordering::SeqCst);
625        if edge_type_id > current {
626            entry.store(edge_type_id, Ordering::SeqCst);
627        }
628
629        self.emit_schema_event(SchemaChangeEvent::EdgeTypeCreated {
630            space_id: space_info.space_id,
631            space_name: space_name.to_string(),
632            edge_type_id,
633            type_name: edge_type.edge_type_name.clone(),
634        });
635
636        Ok(edge_type_id)
637    }
638
639    pub fn drop_edge_type(
640        &self,
641        space_name: &str,
642        edge_type_name: &str,
643    ) -> Result<bool, StorageError> {
644        let space_info = self.get_space(space_name)?.ok_or_else(|| {
645            StorageError::db_error(format!("Space \"{}\" does not exist", space_name))
646        })?;
647
648        let removed: Option<(u32, String)> = {
649            let mut edge_types = self.edge_types.write();
650            let key = edge_types
651                .iter()
652                .find(|((sid, _), data)| {
653                    *sid == space_info.space_id && data.info.edge_type_name == edge_type_name
654                })
655                .map(|(k, _)| *k);
656
657            if let Some(k) = key {
658                edge_types
659                    .remove(&k)
660                    .map(|d| (d.info.edge_type_id, d.info.edge_type_name))
661            } else {
662                None
663            }
664        };
665
666        if let Some((edge_type_id, type_name)) = removed {
667            self.emit_schema_event(SchemaChangeEvent::EdgeTypeDropped {
668                space_id: space_info.space_id,
669                space_name: space_name.to_string(),
670                edge_type_id,
671                type_name,
672            });
673            Ok(true)
674        } else {
675            Ok(false)
676        }
677    }
678
679    pub fn get_edge_type(
680        &self,
681        space_name: &str,
682        edge_type_name: &str,
683    ) -> Result<Option<EdgeTypeInfo>, StorageError> {
684        let space_info = self.get_space(space_name)?.ok_or_else(|| {
685            StorageError::db_error(format!("Space \"{}\" does not exist", space_name))
686        })?;
687
688        let edge_types = self.edge_types.read();
689        Ok(edge_types
690            .iter()
691            .find(|((sid, _), data)| {
692                *sid == space_info.space_id && data.info.edge_type_name == edge_type_name
693            })
694            .map(|(_, data)| data)
695            .map(|d| d.info.clone()))
696    }
697
698    pub fn list_edge_types(&self, space_name: &str) -> Result<Vec<EdgeTypeInfo>, StorageError> {
699        let space_info = self.get_space(space_name)?.ok_or_else(|| {
700            StorageError::db_error(format!("Space \"{}\" does not exist", space_name))
701        })?;
702
703        let edge_types = self.edge_types.read();
704        Ok(edge_types
705            .iter()
706            .filter(|((sid, _), _)| *sid == space_info.space_id)
707            .map(|(_, data)| data.info.clone())
708            .collect())
709    }
710
711    pub fn find_edge_type_by_id(&self, edge_type_id: u32) -> Option<(String, EdgeTypeInfo)> {
712        let edge_types = self.edge_types.read();
713        let spaces = self.spaces.read();
714
715        edge_types
716            .iter()
717            .find_map(|((space_id, current_edge_type_id), data)| {
718                if *current_edge_type_id != edge_type_id {
719                    return None;
720                }
721
722                let space_name = spaces
723                    .get(space_id)
724                    .map(|space| space.info.space_name.clone())?;
725
726                Some((space_name, data.info.clone()))
727            })
728    }
729
730    pub fn update_edge_type(
731        &self,
732        space_name: &str,
733        edge_type: &EdgeTypeInfo,
734    ) -> Result<bool, StorageError> {
735        let space_info = self.get_space(space_name)?.ok_or_else(|| {
736            StorageError::db_error(format!("Space \"{}\" does not exist", space_name))
737        })?;
738
739        let (applied, edge_type_id) = {
740            let mut edge_types = self.edge_types.write();
741            let key = edge_types
742                .iter()
743                .find(|((sid, _), data)| {
744                    *sid == space_info.space_id
745                        && data.info.edge_type_name == edge_type.edge_type_name
746                })
747                .map(|(k, _)| *k);
748
749            if let Some(k) = key {
750                if let Some(data) = edge_types.get_mut(&k) {
751                    let id = data.info.edge_type_id;
752                    data.info = edge_type.clone();
753                    (true, id)
754                } else {
755                    (false, 0)
756                }
757            } else {
758                (false, 0)
759            }
760        };
761        if applied {
762            self.emit_schema_event(SchemaChangeEvent::EdgeTypeAltered {
763                space_id: space_info.space_id,
764                space_name: space_name.to_string(),
765                edge_type_id,
766                type_name: edge_type.edge_type_name.clone(),
767                added_properties: edge_type
768                    .properties
769                    .iter()
770                    .map(|p| p.name.clone())
771                    .collect(),
772                removed_properties: Vec::new(),
773            });
774        }
775        Ok(applied)
776    }
777
778    pub fn alter_tag(
779        &self,
780        space_name: &str,
781        tag_name: &str,
782        additions: Vec<PropertyDef>,
783        deletions: Vec<String>,
784        new_comment: Option<String>,
785    ) -> Result<bool, StorageError> {
786        let space_info = self.get_space(space_name)?.ok_or_else(|| {
787            StorageError::db_error(format!("Space \"{}\" does not exist", space_name))
788        })?;
789
790        let applied: Option<(u32, Vec<String>, Vec<String>)> = {
791            let mut tags = self.tags.write();
792            let tag_key = tags
793                .iter()
794                .find(|((sid, _), data)| {
795                    *sid == space_info.space_id && data.info.tag_name == tag_name
796                })
797                .map(|(k, _)| *k);
798
799            if let Some(key) = tag_key {
800                if let Some(data) = tags.get_mut(&key) {
801                    let added: Vec<String> = additions.iter().map(|p| p.name.clone()).collect();
802                    for prop in additions {
803                        if !data.info.properties.iter().any(|p| p.name == prop.name) {
804                            data.info.properties.push(prop);
805                        }
806                    }
807                    data.info
808                        .properties
809                        .retain(|p| !deletions.contains(&p.name));
810                    if let Some(col) = data.info.ttl_col.clone() {
811                        if !data.info.properties.iter().any(|p| p.name == col) {
812                            data.info.ttl_duration = None;
813                            data.info.ttl_col = None;
814                        }
815                    }
816                    if let Some(comment) = new_comment {
817                        if comment.is_empty() {
818                            data.info.comment = None;
819                        } else {
820                            data.info.comment = Some(comment);
821                        }
822                    }
823                    Some((data.info.tag_id, added, deletions))
824                } else {
825                    None
826                }
827            } else {
828                None
829            }
830        };
831        if let Some((tag_id, added, removed)) = applied {
832            self.emit_schema_event(SchemaChangeEvent::TagAltered {
833                space_id: space_info.space_id,
834                space_name: space_name.to_string(),
835                tag_id,
836                tag_name: tag_name.to_string(),
837                added_properties: added,
838                removed_properties: removed,
839            });
840            Ok(true)
841        } else {
842            Ok(false)
843        }
844    }
845    pub fn rename_tag_property(
846        &self,
847        space_name: &str,
848        tag_name: &str,
849        old_name: &str,
850        new_name: &str,
851    ) -> Result<bool, StorageError> {
852        let space_info = self.get_space(space_name)?.ok_or_else(|| {
853            StorageError::db_error(format!("Space \"{}\" does not exist", space_name))
854        })?;
855
856        let applied: Option<(u32, Vec<String>, Vec<String>)> = {
857            let mut tags = self.tags.write();
858            let tag_key = tags
859                .iter()
860                .find(|((sid, _), data)| {
861                    *sid == space_info.space_id && data.info.tag_name == tag_name
862                })
863                .map(|(k, _)| *k);
864
865            if let Some(key) = tag_key {
866                if let Some(data) = tags.get_mut(&key) {
867                    if let Some(prop) = data.info.properties.iter_mut().find(|p| p.name == old_name)
868                    {
869                        prop.name = new_name.to_string();
870                        if data.info.ttl_col.as_deref() == Some(old_name) {
871                            data.info.ttl_col = Some(new_name.to_string());
872                        }
873                        Some((
874                            data.info.tag_id,
875                            vec![new_name.to_string()],
876                            vec![old_name.to_string()],
877                        ))
878                    } else {
879                        None
880                    }
881                } else {
882                    None
883                }
884            } else {
885                None
886            }
887        };
888        if let Some((tag_id, added, removed)) = applied {
889            self.emit_schema_event(SchemaChangeEvent::TagAltered {
890                space_id: space_info.space_id,
891                space_name: space_name.to_string(),
892                tag_id,
893                tag_name: tag_name.to_string(),
894                added_properties: added,
895                removed_properties: removed,
896            });
897            Ok(true)
898        } else {
899            Ok(false)
900        }
901    }
902
903    pub fn rename_tag(
904        &self,
905        space_name: &str,
906        old_name: &str,
907        new_name: &str,
908    ) -> Result<bool, StorageError> {
909        let space_info = self.get_space(space_name)?.ok_or_else(|| {
910            StorageError::db_error(format!("Space \"{}\" does not exist", space_name))
911        })?;
912
913        let mut tags = self.tags.write();
914
915        let tag_key = tags
916            .iter()
917            .find(|((sid, _), data)| *sid == space_info.space_id && data.info.tag_name == old_name)
918            .map(|(k, _)| *k);
919
920        if let Some(old_key) = tag_key {
921            let data = tags
922                .remove(&old_key)
923                .ok_or_else(|| StorageError::db_error(format!("Tag \"{}\" not found", old_name)))?;
924
925            if tags
926                .iter()
927                .any(|((sid, _), d)| *sid == space_info.space_id && d.info.tag_name == new_name)
928            {
929                tags.insert(old_key, data);
930                return Err(StorageError::db_error(format!(
931                    "Tag \"{}\" already exists",
932                    new_name
933                )));
934            }
935
936            let mut new_data = data;
937            new_data.info.tag_name = new_name.to_string();
938            let renamed_tag_id = new_data.info.tag_id;
939
940            let new_key = (old_key.0, old_key.1);
941            tags.insert(new_key, new_data);
942            drop(tags);
943
944            {
945                let mut edge_types = self.edge_types.write();
946                for ((sid, _), data) in edge_types.iter_mut() {
947                    if *sid == space_info.space_id {
948                        if data.info.src_tag_name == old_name {
949                            data.info.src_tag_name = new_name.to_string();
950                        }
951                        if data.info.dst_tag_name == old_name {
952                            data.info.dst_tag_name = new_name.to_string();
953                        }
954                    }
955                }
956            }
957
958            self.emit_schema_event(SchemaChangeEvent::TagRenamed {
959                space_id: space_info.space_id,
960                space_name: space_name.to_string(),
961                tag_id: renamed_tag_id,
962                old_name: old_name.to_string(),
963                new_name: new_name.to_string(),
964            });
965
966            return Ok(true);
967        }
968
969        Ok(false)
970    }
971
972    pub fn rename_edge_type(
973        &self,
974        space_name: &str,
975        old_name: &str,
976        new_name: &str,
977    ) -> Result<bool, StorageError> {
978        let space_info = self.get_space(space_name)?.ok_or_else(|| {
979            StorageError::db_error(format!("Space \"{}\" does not exist", space_name))
980        })?;
981
982        let renamed: Option<u32> = {
983            let mut edge_types = self.edge_types.write();
984
985            let key = edge_types
986                .iter()
987                .find(|((sid, _), data)| {
988                    *sid == space_info.space_id && data.info.edge_type_name == old_name
989                })
990                .map(|(k, _)| *k);
991
992            if let Some(old_key) = key {
993                let data = edge_types.remove(&old_key).ok_or_else(|| {
994                    StorageError::db_error(format!("Edge type \"{}\" not found", old_name))
995                })?;
996
997                if edge_types.iter().any(|((sid, _), d)| {
998                    *sid == space_info.space_id && d.info.edge_type_name == new_name
999                }) {
1000                    edge_types.insert(old_key, data);
1001                    return Err(StorageError::db_error(format!(
1002                        "Edge type \"{}\" already exists",
1003                        new_name
1004                    )));
1005                }
1006
1007                let mut new_data = data;
1008                new_data.info.edge_type_name = new_name.to_string();
1009                let renamed_id = new_data.info.edge_type_id;
1010
1011                let new_key = (old_key.0, old_key.1);
1012                edge_types.insert(new_key, new_data);
1013
1014                Some(renamed_id)
1015            } else {
1016                None
1017            }
1018        };
1019
1020        if let Some(edge_type_id) = renamed {
1021            self.emit_schema_event(SchemaChangeEvent::EdgeTypeRenamed {
1022                space_id: space_info.space_id,
1023                space_name: space_name.to_string(),
1024                edge_type_id,
1025                old_name: old_name.to_string(),
1026                new_name: new_name.to_string(),
1027            });
1028            return Ok(true);
1029        }
1030
1031        Ok(false)
1032    }
1033
1034    pub fn rename_edge_property(
1035        &self,
1036        space_name: &str,
1037        edge_type_name: &str,
1038        old_name: &str,
1039        new_name: &str,
1040    ) -> Result<bool, StorageError> {
1041        let space_info = self.get_space(space_name)?.ok_or_else(|| {
1042            StorageError::db_error(format!("Space \"{}\" does not exist", space_name))
1043        })?;
1044
1045        let applied: Option<(u32, Vec<String>, Vec<String>)> = {
1046            let mut edge_types = self.edge_types.write();
1047            let key = edge_types
1048                .iter()
1049                .find(|((sid, _), data)| {
1050                    *sid == space_info.space_id && data.info.edge_type_name == edge_type_name
1051                })
1052                .map(|(k, _)| *k);
1053
1054            if let Some(key) = key {
1055                if let Some(data) = edge_types.get_mut(&key) {
1056                    if let Some(prop) = data
1057                        .info
1058                        .properties
1059                        .iter_mut()
1060                        .find(|p| p.name == old_name)
1061                    {
1062                        prop.name = new_name.to_string();
1063                        if data.info.ttl_col.as_deref() == Some(old_name) {
1064                            data.info.ttl_col = Some(new_name.to_string());
1065                        }
1066                        Some((
1067                            data.info.edge_type_id,
1068                            vec![new_name.to_string()],
1069                            vec![old_name.to_string()],
1070                        ))
1071                    } else {
1072                        None
1073                    }
1074                } else {
1075                    None
1076                }
1077            } else {
1078                None
1079            }
1080        };
1081        if let Some((edge_type_id, added, removed)) = applied {
1082            self.emit_schema_event(SchemaChangeEvent::EdgeTypeAltered {
1083                space_id: space_info.space_id,
1084                space_name: space_name.to_string(),
1085                edge_type_id,
1086                type_name: edge_type_name.to_string(),
1087                added_properties: added,
1088                removed_properties: removed,
1089            });
1090            Ok(true)
1091        } else {
1092            Ok(false)
1093        }
1094    }
1095
1096    pub fn alter_edge_type(
1097        &self,
1098        space_name: &str,
1099        edge_type_name: &str,
1100        additions: Vec<PropertyDef>,
1101        deletions: Vec<String>,
1102        new_comment: Option<String>,
1103    ) -> Result<bool, StorageError> {
1104        let space_info = self.get_space(space_name)?.ok_or_else(|| {
1105            StorageError::db_error(format!("Space \"{}\" does not exist", space_name))
1106        })?;
1107
1108        let applied: Option<(u32, Vec<String>, Vec<String>)> = {
1109            let mut edge_types = self.edge_types.write();
1110            let key = edge_types
1111                .iter()
1112                .find(|((sid, _), data)| {
1113                    *sid == space_info.space_id && data.info.edge_type_name == edge_type_name
1114                })
1115                .map(|(k, _)| *k);
1116
1117            if let Some(k) = key {
1118                if let Some(data) = edge_types.get_mut(&k) {
1119                    let added: Vec<String> = additions.iter().map(|p| p.name.clone()).collect();
1120                    for prop in additions {
1121                        if !data.info.properties.iter().any(|p| p.name == prop.name) {
1122                            data.info.properties.push(prop);
1123                        }
1124                    }
1125                    data.info
1126                        .properties
1127                        .retain(|p| !deletions.contains(&p.name));
1128                    if let Some(col) = data.info.ttl_col.clone() {
1129                        if !data.info.properties.iter().any(|p| p.name == col) {
1130                            data.info.ttl_duration = None;
1131                            data.info.ttl_col = None;
1132                        }
1133                    }
1134                    if let Some(comment) = new_comment {
1135                        if comment.is_empty() {
1136                            data.info.comment = None;
1137                        } else {
1138                            data.info.comment = Some(comment);
1139                        }
1140                    }
1141                    Some((data.info.edge_type_id, added, deletions))
1142                } else {
1143                    None
1144                }
1145            } else {
1146                None
1147            }
1148        };
1149        if let Some((edge_type_id, added, removed)) = applied {
1150            self.emit_schema_event(SchemaChangeEvent::EdgeTypeAltered {
1151                space_id: space_info.space_id,
1152                space_name: space_name.to_string(),
1153                edge_type_id,
1154                type_name: edge_type_name.to_string(),
1155                added_properties: added,
1156                removed_properties: removed,
1157            });
1158            return Ok(true);
1159        }
1160        Ok(false)
1161    }
1162
1163    pub fn update_edge_endpoints(
1164        &self,
1165        space_name: &str,
1166        edge_type_name: &str,
1167        src_tag_name: &str,
1168        dst_tag_name: &str,
1169    ) -> Result<bool, StorageError> {
1170        let space_info = self.get_space(space_name)?.ok_or_else(|| {
1171            StorageError::db_error(format!("Space \"{}\" does not exist", space_name))
1172        })?;
1173
1174        let applied: Option<u32> = {
1175            let mut edge_types = self.edge_types.write();
1176            let key = edge_types
1177                .iter()
1178                .find(|((sid, _), data)| {
1179                    *sid == space_info.space_id && data.info.edge_type_name == edge_type_name
1180                })
1181                .map(|(k, _)| *k);
1182
1183            if let Some(k) = key {
1184                if let Some(data) = edge_types.get_mut(&k) {
1185                    data.info.src_tag_name = src_tag_name.to_string();
1186                    data.info.dst_tag_name = dst_tag_name.to_string();
1187                    Some(data.info.edge_type_id)
1188                } else {
1189                    None
1190                }
1191            } else {
1192                None
1193            }
1194        };
1195        if let Some(edge_type_id) = applied {
1196            self.emit_schema_event(SchemaChangeEvent::EdgeTypeAltered {
1197                space_id: space_info.space_id,
1198                space_name: space_name.to_string(),
1199                edge_type_id,
1200                type_name: edge_type_name.to_string(),
1201                added_properties: Vec::new(),
1202                removed_properties: Vec::new(),
1203            });
1204            return Ok(true);
1205        }
1206        Ok(false)
1207    }
1208
1209    pub fn save_schema(&self, path: &Path) -> Result<(), StorageError> {
1210        use std::fs::{self, File};
1211        use std::io::Write;
1212
1213        if let Some(parent) = path.parent() {
1214            fs::create_dir_all(parent).map_err(|e| StorageError::io_error(e.to_string()))?;
1215        }
1216
1217        let spaces: Vec<SpaceInfo> = self
1218            .spaces
1219            .read()
1220            .values()
1221            .map(|d| d.info.clone())
1222            .collect();
1223
1224        let tags: Vec<(u64, TagInfo)> = self
1225            .tags
1226            .read()
1227            .iter()
1228            .map(|((space_id, _), data)| (*space_id, data.info.clone()))
1229            .collect();
1230
1231        let edge_types: Vec<(u64, EdgeTypeInfo)> = self
1232            .edge_types
1233            .read()
1234            .iter()
1235            .map(|((space_id, _), data)| (*space_id, data.info.clone()))
1236            .collect();
1237
1238        let space_id_counter = self
1239            .space_id_counter
1240            .load(std::sync::atomic::Ordering::SeqCst);
1241
1242        let tag_id_counters: Vec<(u64, u32)> = self
1243            .tag_id_counter
1244            .iter()
1245            .map(|entry| (*entry.key(), entry.value().load(Ordering::SeqCst)))
1246            .collect();
1247
1248        let edge_type_id_counters: Vec<(u64, u32)> = self
1249            .edge_type_id_counter
1250            .iter()
1251            .map(|entry| (*entry.key(), entry.value().load(Ordering::SeqCst)))
1252            .collect();
1253
1254        let snapshot = SchemaSnapshot {
1255            version: SCHEMA_FORMAT_VERSION,
1256            spaces,
1257            tags,
1258            edge_types,
1259            space_id_counter,
1260            tag_id_counters,
1261            edge_type_id_counters,
1262            serial_next: self.serial_next.read().clone(),
1263            sequences: self.sequences.read().values().cloned().collect(),
1264        };
1265
1266        let json = serde_json::to_string_pretty(&snapshot)
1267            .map_err(|e| StorageError::serialize_error(e.to_string()))?;
1268
1269        let mut file = File::create(path).map_err(|e| StorageError::io_error(e.to_string()))?;
1270
1271        file.write_all(json.as_bytes())
1272            .map_err(|e| StorageError::io_error(e.to_string()))?;
1273
1274        Ok(())
1275    }
1276
1277    pub fn load_schema(&self, path: &Path) -> Result<(), StorageError> {
1278        use std::fs::File;
1279        use std::io::Read;
1280
1281        if !path.exists() {
1282            return Ok(());
1283        }
1284
1285        let mut file = File::open(path).map_err(|e| StorageError::io_error(e.to_string()))?;
1286
1287        let mut json = String::new();
1288        file.read_to_string(&mut json)
1289            .map_err(|e| StorageError::io_error(e.to_string()))?;
1290
1291        let snapshot: SchemaSnapshot = serde_json::from_str(&json)
1292            .map_err(|e| StorageError::deserialize_error(e.to_string()))?;
1293
1294        if snapshot.version > SCHEMA_FORMAT_VERSION {
1295            return Err(StorageError::deserialize_error(format!(
1296                "Schema version {} is newer than supported version {}",
1297                snapshot.version, SCHEMA_FORMAT_VERSION
1298            )));
1299        }
1300
1301        self.spaces.write().clear();
1302        self.space_name_index.write().clear();
1303        self.tags.write().clear();
1304        self.edge_types.write().clear();
1305        self.tag_id_counter.clear();
1306        self.edge_type_id_counter.clear();
1307
1308        let max_tag_counter = snapshot
1309            .tag_id_counters
1310            .iter()
1311            .map(|(_, counter)| *counter)
1312            .max()
1313            .unwrap_or(0)
1314            .max(
1315                snapshot
1316                    .tags
1317                    .iter()
1318                    .map(|(_, tag)| tag.tag_id)
1319                    .max()
1320                    .unwrap_or(0),
1321            );
1322        let max_edge_type_counter = snapshot
1323            .edge_type_id_counters
1324            .iter()
1325            .map(|(_, counter)| *counter)
1326            .max()
1327            .unwrap_or(0)
1328            .max(
1329                snapshot
1330                    .edge_types
1331                    .iter()
1332                    .map(|(_, edge_type)| edge_type.edge_type_id)
1333                    .max()
1334                    .unwrap_or(0),
1335            );
1336
1337        for space in snapshot.spaces {
1338            self.space_name_index
1339                .write()
1340                .insert(space.space_name.clone(), space.space_id);
1341            self.spaces
1342                .write()
1343                .insert(space.space_id, SpaceData { info: space });
1344        }
1345
1346        for (space_id, tag) in snapshot.tags {
1347            self.tags
1348                .write()
1349                .insert((space_id, tag.tag_id), TagData { info: tag });
1350        }
1351
1352        for (space_id, edge_type) in snapshot.edge_types {
1353            self.edge_types.write().insert(
1354                (space_id, edge_type.edge_type_id),
1355                EdgeTypeData { info: edge_type },
1356            );
1357        }
1358
1359        self.space_id_counter
1360            .store(snapshot.space_id_counter, Ordering::SeqCst);
1361
1362        for (space_id, counter) in snapshot.tag_id_counters {
1363            self.tag_id_counter
1364                .insert(space_id, AtomicU32::new(counter));
1365        }
1366        self.tag_id_counter
1367            .insert(0, AtomicU32::new(max_tag_counter));
1368
1369        for (space_id, counter) in snapshot.edge_type_id_counters {
1370            self.edge_type_id_counter
1371                .insert(space_id, AtomicU32::new(counter));
1372        }
1373        self.edge_type_id_counter
1374            .insert(0, AtomicU32::new(max_edge_type_counter));
1375
1376        *self.serial_next.write() = snapshot.serial_next;
1377
1378        let mut sequences = self.sequences.write();
1379        sequences.clear();
1380        for def in snapshot.sequences {
1381            sequences.insert(def.name.clone(), def);
1382        }
1383
1384        Ok(())
1385    }
1386
1387    // ==================== Sequence Operations ====================
1388
1389    /// Create a sequence
1390    pub fn create_sequence(
1391        &self,
1392        name: String,
1393        start: i64,
1394        increment: i64,
1395        min_value: i64,
1396        max_value: i64,
1397        cycle: bool,
1398    ) -> Result<(), StorageError> {
1399        {
1400            let mut sequences = self.sequences.write();
1401            if sequences.contains_key(&name) {
1402                return Err(StorageError::db_error(format!(
1403                    "Sequence '{}' already exists",
1404                    name
1405                )));
1406            }
1407            let def = SequenceDef::new(name.clone(), start, increment, min_value, max_value, cycle);
1408            sequences.insert(name.clone(), def);
1409        }
1410        self.emit_schema_event(SchemaChangeEvent::SequenceCreated { name });
1411        Ok(())
1412    }
1413
1414    /// Drop a sequence
1415    pub fn drop_sequence(&self, name: &str) -> Result<bool, StorageError> {
1416        let removed = {
1417            let mut sequences = self.sequences.write();
1418            sequences.remove(name).is_some()
1419        };
1420        if removed {
1421            self.emit_schema_event(SchemaChangeEvent::SequenceDropped {
1422                name: name.to_string(),
1423            });
1424        }
1425        Ok(removed)
1426    }
1427
1428    /// Get a sequence definition
1429    pub fn get_sequence(&self, name: &str) -> Option<SequenceDef> {
1430        let sequences = self.sequences.read();
1431        sequences.get(name).cloned()
1432    }
1433
1434    /// Get the current value of a sequence
1435    pub fn sequence_current_value(&self, name: &str) -> Result<i64, StorageError> {
1436        let sequences = self.sequences.read();
1437        let def = sequences
1438            .get(name)
1439            .ok_or_else(|| StorageError::db_error(format!("Sequence '{}' does not exist", name)))?;
1440        Ok(def.current_value())
1441    }
1442
1443    /// Get the next value of a sequence (atomic increment)
1444    pub fn sequence_next_value(&self, name: &str) -> Result<i64, StorageError> {
1445        let sequences = self.sequences.read();
1446        let def = sequences
1447            .get(name)
1448            .ok_or_else(|| StorageError::db_error(format!("Sequence '{}' does not exist", name)))?;
1449        def.next_value()
1450    }
1451
1452    /// Alter sequence properties
1453    pub fn alter_sequence(
1454        &self,
1455        name: &str,
1456        increment: Option<i64>,
1457        min_value: Option<i64>,
1458        max_value: Option<i64>,
1459        cycle: Option<bool>,
1460    ) -> Result<(), StorageError> {
1461        {
1462            let mut sequences = self.sequences.write();
1463            let def = sequences.get(name).ok_or_else(|| {
1464                StorageError::db_error(format!("Sequence '{}' does not exist", name))
1465            })?;
1466
1467            let new_increment = increment.unwrap_or(def.increment);
1468            let new_min = min_value.unwrap_or(def.min_value);
1469            let new_max = max_value.unwrap_or(def.max_value);
1470            let new_cycle = cycle.unwrap_or(def.cycle);
1471            let current = def.current_value();
1472
1473            let new_def = SequenceDef::new(
1474                name.to_string(),
1475                current,
1476                new_increment,
1477                new_min,
1478                new_max,
1479                new_cycle,
1480            );
1481            sequences.insert(name.to_string(), new_def);
1482        }
1483        self.emit_schema_event(SchemaChangeEvent::SequenceAltered {
1484            name: name.to_string(),
1485        });
1486        Ok(())
1487    }
1488
1489    /// List all sequence names
1490    pub fn list_sequences(&self) -> Vec<String> {
1491        let sequences = self.sequences.read();
1492        sequences.keys().cloned().collect()
1493    }
1494}
1495
1496impl Default for SchemaManager {
1497    fn default() -> Self {
1498        Self::new()
1499    }
1500}
1501
1502#[cfg(test)]
1503mod tests {
1504    use super::*;
1505    use crate::types::{EdgeTypeInfo, SpaceInfo, TagInfo};
1506
1507    #[test]
1508    fn schema_names_are_scoped_by_space() {
1509        let manager = SchemaManager::new();
1510        let mut first = SpaceInfo::new("first".to_string());
1511        let mut second = SpaceInfo::new("second".to_string());
1512        manager
1513            .create_space(&mut first)
1514            .expect("create first space");
1515        manager
1516            .create_space(&mut second)
1517            .expect("create second space");
1518
1519        let first_tag_id = manager
1520            .create_tag("first", &TagInfo::new("person".to_string()))
1521            .expect("create first tag");
1522        let second_tag_id = manager
1523            .create_tag("second", &TagInfo::new("person".to_string()))
1524            .expect("create second tag");
1525
1526        assert_ne!(first_tag_id, second_tag_id);
1527        assert_eq!(
1528            manager
1529                .get_tag("first", "person")
1530                .expect("get first tag")
1531                .expect("first tag exists")
1532                .tag_id,
1533            first_tag_id
1534        );
1535        assert_eq!(
1536            manager
1537                .get_tag("second", "person")
1538                .expect("get second tag")
1539                .expect("second tag exists")
1540                .tag_id,
1541            second_tag_id
1542        );
1543
1544        let first_edge_id = manager
1545            .create_edge_type("first", &EdgeTypeInfo::new("knows".to_string()))
1546            .expect("create first edge");
1547        let second_edge_id = manager
1548            .create_edge_type("second", &EdgeTypeInfo::new("knows".to_string()))
1549            .expect("create second edge");
1550
1551        assert_ne!(first_edge_id, second_edge_id);
1552        assert_eq!(
1553            manager
1554                .get_edge_type("first", "knows")
1555                .expect("get first edge")
1556                .expect("first edge exists")
1557                .edge_type_id,
1558            first_edge_id
1559        );
1560        assert_eq!(
1561            manager
1562                .get_edge_type("second", "knows")
1563                .expect("get second edge")
1564                .expect("second edge exists")
1565                .edge_type_id,
1566            second_edge_id
1567        );
1568    }
1569}