1#![forbid(unsafe_code)]
8
9use crate::catalog::TableCatalog;
10use crate::constraints::{
11 ConstraintId, ConstraintKind, ConstraintRecord, ConstraintState, ForeignKeyAction,
12 ForeignKeyConstraint, PrimaryKeyConstraint, UniqueConstraint,
13};
14use crate::constraints::{ForeignKeyTableInfo, ValidatedForeignKey, validate_foreign_keys};
15use crate::reserved;
16use crate::resolvers::resolve_table_name;
17use crate::sys_catalog::{ConstraintNameRecord, SysCatalog};
18use crate::sys_catalog::{MultiColumnIndexEntryMeta, SingleColumnIndexEntryMeta, TriggerEntryMeta};
19use crate::table::Table;
20use crate::types::{FieldId, TableColumn, TableId};
21use crate::view::{ForeignKeyView, TableView};
22use crate::{ColMeta, TableMeta, TableMultiColumnIndexMeta};
23use arrow::datatypes::DataType;
24use llkv_column_map::ColumnStore;
25use llkv_column_map::store::IndexKind;
26use llkv_column_map::types::LogicalFieldId;
27use llkv_plan::ForeignKeySpec;
28use llkv_result::{Error, Result as LlkvResult};
29use llkv_storage::pager::Pager;
30use rustc_hash::{FxHashMap, FxHashSet};
31use simd_r_drive_entry_handle::EntryHandle;
32use std::sync::{Arc, RwLock};
33
34#[derive(Clone, Debug, Default, PartialEq, Eq)]
35pub struct SingleColumnIndexEntry {
36 pub index_name: String,
37 pub canonical_name: String,
38 pub column_id: FieldId,
39 pub column_name: String,
40 pub unique: bool,
41 pub ascending: bool,
42 pub nulls_first: bool,
43}
44
45#[derive(Clone, Debug, Default)]
46struct TableSnapshot {
47 table_meta: Option<TableMeta>,
48 column_metas: FxHashMap<FieldId, ColMeta>,
49 constraints: FxHashMap<ConstraintId, ConstraintRecord>,
50 constraint_names: FxHashMap<ConstraintId, String>,
51 single_indexes: FxHashMap<String, SingleColumnIndexEntry>,
52 multi_column_indexes: FxHashMap<String, MultiColumnIndexEntryMeta>,
53 sort_indexes: FxHashSet<FieldId>,
54 triggers: FxHashMap<String, TriggerEntryMeta>,
55}
56
57#[derive(Clone, Debug)]
58struct TableState {
59 current: TableSnapshot,
60 persisted: TableSnapshot,
61}
62
63impl TableState {
64 fn from_snapshot(snapshot: TableSnapshot) -> Self {
65 Self {
66 current: snapshot.clone(),
67 persisted: snapshot,
68 }
69 }
70}
71
72#[derive(Default)]
73struct ReferencingIndex {
74 parent_to_children: FxHashMap<TableId, FxHashSet<(TableId, ConstraintId)>>,
75 child_to_parents: FxHashMap<TableId, FxHashSet<TableId>>,
76 initialized: bool,
77}
78
79impl ReferencingIndex {
80 fn remove_child(&mut self, child_id: TableId) {
81 if let Some(parents) = self.child_to_parents.remove(&child_id) {
82 for parent_id in parents {
83 if let Some(children) = self.parent_to_children.get_mut(&parent_id) {
84 children.retain(|(entry_child, _)| *entry_child != child_id);
85 if children.is_empty() {
86 self.parent_to_children.remove(&parent_id);
87 }
88 }
89 }
90 }
91 }
92
93 fn insert(&mut self, parent_id: TableId, child_id: TableId, constraint_id: ConstraintId) {
94 self.parent_to_children
95 .entry(parent_id)
96 .or_default()
97 .insert((child_id, constraint_id));
98 self.child_to_parents
99 .entry(child_id)
100 .or_default()
101 .insert(parent_id);
102 self.initialized = true;
103 }
104
105 fn children(&self, parent_id: TableId) -> Vec<(TableId, ConstraintId)> {
106 self.parent_to_children
107 .get(&parent_id)
108 .map(|set| set.iter().cloned().collect())
109 .unwrap_or_default()
110 }
111
112 fn mark_initialized(&mut self) {
113 self.initialized = true;
114 }
115
116 fn is_initialized(&self) -> bool {
117 self.initialized
118 }
119}
120
121pub struct MetadataManager<P>
123where
124 P: Pager<Blob = EntryHandle> + Send + Sync,
125{
126 store: Arc<ColumnStore<P>>,
127 tables: RwLock<FxHashMap<TableId, TableState>>,
128 referencing_index: RwLock<ReferencingIndex>,
129}
130
131impl<P> MetadataManager<P>
132where
133 P: Pager<Blob = EntryHandle> + Send + Sync,
134{
135 pub fn new(store: Arc<ColumnStore<P>>) -> Self {
137 Self {
138 store,
139 tables: RwLock::new(FxHashMap::default()),
140 referencing_index: RwLock::new(ReferencingIndex::default()),
141 }
142 }
143
144 fn ensure_table_state(&self, table_id: TableId) -> LlkvResult<()> {
146 if self.tables.read().unwrap().contains_key(&table_id) {
147 return Ok(());
148 }
149 let state = self.load_table_state(table_id)?;
150 {
151 let mut tables = self.tables.write().unwrap();
152 tables.entry(table_id).or_insert(state);
153 }
154 self.refresh_referencing_index_for_table(table_id);
155 Ok(())
156 }
157
158 fn load_table_state(&self, table_id: TableId) -> LlkvResult<TableState> {
159 let catalog = SysCatalog::new(&self.store);
160 let table_meta = catalog.get_table_meta(table_id);
161 let constraint_records = catalog.constraint_records_for_table(table_id)?;
162 let constraint_ids: Vec<ConstraintId> = constraint_records
163 .iter()
164 .map(|record| record.constraint_id)
165 .collect();
166 let constraint_name_entries = if constraint_ids.is_empty() {
167 Vec::new()
168 } else {
169 catalog.get_constraint_names(table_id, &constraint_ids)?
170 };
171 let multi_uniques = catalog.get_multi_column_indexes(table_id)?;
172 let single_index_metas = catalog.get_single_column_indexes(table_id)?;
173 let multi_column_index_metas = catalog.get_multi_column_indexes(table_id)?;
174 let trigger_metas = catalog.get_triggers(table_id)?;
175 let mut constraints = FxHashMap::default();
176 let mut constraint_names = FxHashMap::default();
177 let mut single_indexes = FxHashMap::default();
178 let mut multi_column_indexes = FxHashMap::default();
179 let mut sort_indexes = FxHashSet::default();
180 for meta in single_index_metas {
181 sort_indexes.insert(meta.column_id);
182 single_indexes.insert(
183 meta.canonical_name.clone(),
184 SingleColumnIndexEntry {
185 index_name: meta.index_name,
186 canonical_name: meta.canonical_name,
187 column_id: meta.column_id,
188 column_name: meta.column_name,
189 unique: meta.unique,
190 ascending: meta.ascending,
191 nulls_first: meta.nulls_first,
192 },
193 );
194 }
195 for meta in multi_uniques {
196 multi_column_indexes.insert(meta.canonical_name.clone(), meta);
197 }
198 for meta in multi_column_index_metas {
199 multi_column_indexes.insert(meta.canonical_name.clone(), meta);
200 }
201 let mut triggers = FxHashMap::default();
202 for meta in trigger_metas {
203 triggers.insert(meta.canonical_name.clone(), meta);
204 }
205 for (record, name) in constraint_records
206 .into_iter()
207 .zip(constraint_name_entries.into_iter())
208 {
209 if let Some(name) = name {
210 constraint_names.insert(record.constraint_id, name);
211 }
212 constraints.insert(record.constraint_id, record);
213 }
214 let snapshot = TableSnapshot {
215 table_meta,
216 column_metas: FxHashMap::default(),
217 constraints,
218 constraint_names,
219 single_indexes,
220 multi_column_indexes,
221 sort_indexes,
222 triggers,
223 };
224 Ok(TableState::from_snapshot(snapshot))
225 }
226
227 fn refresh_referencing_index_for_table(&self, table_id: TableId) {
228 let foreign_keys: Vec<(TableId, ConstraintId)> = {
229 let tables = self.tables.read().unwrap();
230 match tables.get(&table_id) {
231 Some(state) => state
232 .current
233 .constraints
234 .iter()
235 .filter(|(_, record)| record.is_active())
236 .filter_map(|(constraint_id, record)| {
237 if let ConstraintKind::ForeignKey(fk) = &record.kind {
238 Some((fk.referenced_table, *constraint_id))
239 } else {
240 None
241 }
242 })
243 .collect(),
244 None => Vec::new(),
245 }
246 };
247
248 let mut index = self.referencing_index.write().unwrap();
249 index.remove_child(table_id);
250 for (parent_table, constraint_id) in foreign_keys {
251 index.insert(parent_table, table_id, constraint_id);
252 }
253 }
254
255 fn constraint_name_for(
256 &self,
257 table_id: TableId,
258 constraint_id: ConstraintId,
259 ) -> LlkvResult<Option<String>> {
260 self.ensure_table_state(table_id)?;
261 let tables = self.tables.read().unwrap();
262 let state = tables.get(&table_id).unwrap();
263 Ok(state.current.constraint_names.get(&constraint_id).cloned())
264 }
265
266 fn ensure_referencing_index_initialized(&self) -> LlkvResult<()> {
267 let needs_init = {
268 let index = self.referencing_index.read().unwrap();
269 !index.is_initialized()
270 };
271
272 if !needs_init {
273 return Ok(());
274 }
275
276 let metas = self.all_table_metas()?;
277 for (table_id, _) in metas {
278 self.ensure_table_state(table_id)?;
279 self.refresh_referencing_index_for_table(table_id);
280 }
281
282 let mut index = self.referencing_index.write().unwrap();
283 index.mark_initialized();
284 Ok(())
285 }
286
287 pub fn table_meta(&self, table_id: TableId) -> LlkvResult<Option<TableMeta>> {
289 self.ensure_table_state(table_id)?;
290 let tables = self.tables.read().unwrap();
291 Ok(tables
292 .get(&table_id)
293 .and_then(|state| state.current.table_meta.clone()))
294 }
295
296 pub fn foreign_keys_referencing(
298 &self,
299 referenced_table: TableId,
300 ) -> LlkvResult<Vec<(TableId, ConstraintId)>> {
301 self.ensure_referencing_index_initialized()?;
302 let index = self.referencing_index.read().unwrap();
303 Ok(index.children(referenced_table))
304 }
305
306 pub fn set_table_meta(&self, table_id: TableId, meta: TableMeta) -> LlkvResult<()> {
308 self.ensure_table_state(table_id)?;
309 let mut tables = self.tables.write().unwrap();
310 let state = tables.get_mut(&table_id).unwrap();
311 state.current.table_meta = Some(meta);
312 Ok(())
313 }
314
315 pub fn column_metas(
317 &self,
318 table_id: TableId,
319 field_ids: &[FieldId],
320 ) -> LlkvResult<Vec<Option<ColMeta>>> {
321 self.ensure_table_state(table_id)?;
322
323 let missing_ids = {
325 let tables = self.tables.read().unwrap();
326 let state = tables.get(&table_id).unwrap();
327 field_ids
328 .iter()
329 .copied()
330 .filter(|field_id| !state.current.column_metas.contains_key(field_id))
331 .collect::<Vec<_>>()
332 };
333
334 if !missing_ids.is_empty() {
335 let catalog = SysCatalog::new(&self.store);
336 let fetched = catalog.get_cols_meta(table_id, &missing_ids);
337 let mut tables = self.tables.write().unwrap();
338 let state = tables.get_mut(&table_id).unwrap();
339 for (idx, field_id) in missing_ids.iter().enumerate() {
340 if let Some(meta) = fetched[idx].clone() {
341 state.current.column_metas.insert(*field_id, meta.clone());
342 state
343 .persisted
344 .column_metas
345 .entry(*field_id)
346 .or_insert(meta);
347 }
348 }
349 }
350
351 let tables = self.tables.read().unwrap();
352 let state = tables.get(&table_id).unwrap();
353 Ok(field_ids
354 .iter()
355 .map(|field_id| state.current.column_metas.get(field_id).cloned())
356 .collect())
357 }
358
359 pub fn set_column_meta(&self, table_id: TableId, meta: ColMeta) -> LlkvResult<()> {
361 self.ensure_table_state(table_id)?;
362 let mut tables = self.tables.write().unwrap();
363 let state = tables.get_mut(&table_id).unwrap();
364 state.current.column_metas.insert(meta.col_id, meta);
365 Ok(())
366 }
367
368 pub fn multi_column_uniques(
370 &self,
371 table_id: TableId,
372 ) -> LlkvResult<Vec<MultiColumnIndexEntryMeta>> {
373 self.ensure_table_state(table_id)?;
374 let tables = self.tables.read().unwrap();
375 let state = tables.get(&table_id).unwrap();
376 Ok(state
377 .current
378 .multi_column_indexes
379 .values()
380 .filter(|entry| entry.unique)
381 .cloned()
382 .collect())
383 }
384
385 pub fn set_multi_column_uniques(
387 &self,
388 table_id: TableId,
389 uniques: Vec<MultiColumnIndexEntryMeta>,
390 ) -> LlkvResult<()> {
391 self.ensure_table_state(table_id)?;
392 let mut tables = self.tables.write().unwrap();
393 let state = tables.get_mut(&table_id).unwrap();
394 state
396 .current
397 .multi_column_indexes
398 .retain(|_, entry| !entry.unique);
399 for unique in uniques {
401 state
402 .current
403 .multi_column_indexes
404 .insert(unique.canonical_name.clone(), unique);
405 }
406 Ok(())
407 }
408
409 pub fn single_column_indexes(
411 &self,
412 table_id: TableId,
413 ) -> LlkvResult<Vec<SingleColumnIndexEntry>> {
414 self.ensure_table_state(table_id)?;
415 let tables = self.tables.read().unwrap();
416 let state = tables.get(&table_id).unwrap();
417 Ok(state.current.single_indexes.values().cloned().collect())
418 }
419
420 pub fn single_column_index(
422 &self,
423 table_id: TableId,
424 canonical_index_name: &str,
425 ) -> LlkvResult<Option<SingleColumnIndexEntry>> {
426 self.ensure_table_state(table_id)?;
427 let tables = self.tables.read().unwrap();
428 let state = tables.get(&table_id).unwrap();
429 Ok(state
430 .current
431 .single_indexes
432 .get(canonical_index_name)
433 .cloned())
434 }
435
436 pub fn put_single_column_index(
438 &self,
439 table_id: TableId,
440 entry: SingleColumnIndexEntry,
441 ) -> LlkvResult<()> {
442 self.ensure_table_state(table_id)?;
443 let mut tables = self.tables.write().unwrap();
444 let state = tables.get_mut(&table_id).unwrap();
445 let column_id = entry.column_id;
446 state
447 .current
448 .single_indexes
449 .insert(entry.canonical_name.clone(), entry);
450 state.current.sort_indexes.insert(column_id);
451 Ok(())
452 }
453
454 pub fn remove_single_column_index(
456 &self,
457 table_id: TableId,
458 canonical_index_name: &str,
459 ) -> LlkvResult<Option<SingleColumnIndexEntry>> {
460 self.ensure_table_state(table_id)?;
461 let mut tables = self.tables.write().unwrap();
462 let state = tables.get_mut(&table_id).unwrap();
463 let removed = state.current.single_indexes.remove(canonical_index_name);
464 if let Some(ref entry) = removed {
465 let still_indexed = state
466 .current
467 .single_indexes
468 .values()
469 .any(|existing| existing.column_id == entry.column_id);
470 if !still_indexed {
471 state.current.sort_indexes.remove(&entry.column_id);
472 }
473 }
474 Ok(removed)
475 }
476
477 pub fn put_multi_column_index(
479 &self,
480 table_id: TableId,
481 entry: MultiColumnIndexEntryMeta,
482 ) -> LlkvResult<()> {
483 self.ensure_table_state(table_id)?;
484 let mut tables = self.tables.write().unwrap();
485 let state = tables.get_mut(&table_id).unwrap();
486 state
487 .current
488 .multi_column_indexes
489 .insert(entry.canonical_name.clone(), entry);
490 Ok(())
491 }
492
493 pub fn remove_multi_column_index(
495 &self,
496 table_id: TableId,
497 canonical_index_name: &str,
498 ) -> LlkvResult<Option<MultiColumnIndexEntryMeta>> {
499 self.ensure_table_state(table_id)?;
500 let mut tables = self.tables.write().unwrap();
501 let state = tables.get_mut(&table_id).unwrap();
502 Ok(state
503 .current
504 .multi_column_indexes
505 .remove(canonical_index_name))
506 }
507
508 pub fn get_multi_column_index(
510 &self,
511 table_id: TableId,
512 canonical_index_name: &str,
513 ) -> LlkvResult<Option<MultiColumnIndexEntryMeta>> {
514 self.ensure_table_state(table_id)?;
515 let tables = self.tables.read().unwrap();
516 Ok(tables.get(&table_id).and_then(|state| {
517 state
518 .current
519 .multi_column_indexes
520 .get(canonical_index_name)
521 .cloned()
522 }))
523 }
524
525 pub fn register_sort_index(&self, table_id: TableId, field_id: FieldId) -> LlkvResult<()> {
527 self.ensure_table_state(table_id)?;
528
529 {
530 let mut tables = self.tables.write().unwrap();
531 let state = tables.get_mut(&table_id).unwrap();
532 if state.persisted.sort_indexes.contains(&field_id)
533 || state.current.sort_indexes.contains(&field_id)
534 {
535 state.current.sort_indexes.insert(field_id);
536 return Ok(());
537 }
538 }
539
540 if self.field_has_sort_index(table_id, field_id)? {
541 let mut tables = self.tables.write().unwrap();
542 let state = tables.get_mut(&table_id).unwrap();
543 state.persisted.sort_indexes.insert(field_id);
544 state.current.sort_indexes.insert(field_id);
545 return Ok(());
546 }
547
548 let mut tables = self.tables.write().unwrap();
549 let state = tables.get_mut(&table_id).unwrap();
550 state.current.sort_indexes.insert(field_id);
551 Ok(())
552 }
553
554 pub fn unregister_sort_index(&self, table_id: TableId, field_id: FieldId) -> LlkvResult<()> {
556 self.ensure_table_state(table_id)?;
557
558 let mut tables = self.tables.write().unwrap();
559 let state = tables.get_mut(&table_id).unwrap();
560 state.current.sort_indexes.remove(&field_id);
561
562 if !state.persisted.sort_indexes.contains(&field_id) {
563 drop(tables);
564 if self.field_has_sort_index(table_id, field_id)? {
565 let mut tables = self.tables.write().unwrap();
566 let state = tables.get_mut(&table_id).unwrap();
567 state.persisted.sort_indexes.insert(field_id);
568 }
569 }
570
571 Ok(())
572 }
573
574 pub fn update_multi_column_uniques<F, T>(&self, table_id: TableId, f: F) -> LlkvResult<T>
576 where
577 F: FnOnce(&mut Vec<MultiColumnIndexEntryMeta>) -> T,
578 {
579 self.ensure_table_state(table_id)?;
580 let mut tables = self.tables.write().unwrap();
581 let state = tables.get_mut(&table_id).unwrap();
582 let mut uniques: Vec<MultiColumnIndexEntryMeta> = state
583 .current
584 .multi_column_indexes
585 .values()
586 .filter(|entry| entry.unique)
587 .cloned()
588 .collect();
589 let result = f(&mut uniques);
590 state
592 .current
593 .multi_column_indexes
594 .retain(|_, entry| !entry.unique);
595 for unique in uniques {
597 state
598 .current
599 .multi_column_indexes
600 .insert(unique.canonical_name.clone(), unique);
601 }
602 Ok(result)
603 }
604
605 pub fn triggers(&self, table_id: TableId) -> LlkvResult<Vec<TriggerEntryMeta>> {
607 self.ensure_table_state(table_id)?;
608 let tables = self.tables.read().unwrap();
609 let state = tables.get(&table_id).unwrap();
610 Ok(state.current.triggers.values().cloned().collect())
611 }
612
613 pub fn trigger(
615 &self,
616 table_id: TableId,
617 canonical_name: &str,
618 ) -> LlkvResult<Option<TriggerEntryMeta>> {
619 self.ensure_table_state(table_id)?;
620 let tables = self.tables.read().unwrap();
621 let state = tables.get(&table_id).unwrap();
622 Ok(state
623 .current
624 .triggers
625 .get(&canonical_name.to_ascii_lowercase())
626 .cloned())
627 }
628
629 pub fn insert_trigger(&self, table_id: TableId, trigger: TriggerEntryMeta) -> LlkvResult<()> {
631 self.ensure_table_state(table_id)?;
632 let mut tables = self.tables.write().unwrap();
633 let state = tables.get_mut(&table_id).unwrap();
634 state
635 .current
636 .triggers
637 .insert(trigger.canonical_name.clone(), trigger);
638 Ok(())
639 }
640
641 pub fn remove_trigger(&self, table_id: TableId, canonical_name: &str) -> LlkvResult<bool> {
643 self.ensure_table_state(table_id)?;
644 let mut tables = self.tables.write().unwrap();
645 let state = tables.get_mut(&table_id).unwrap();
646 Ok(state
647 .current
648 .triggers
649 .remove(&canonical_name.to_ascii_lowercase())
650 .is_some())
651 }
652
653 pub fn prepare_table_drop(&self, table_id: TableId, column_ids: &[FieldId]) -> LlkvResult<()> {
658 if !column_ids.is_empty() {
659 let _ = self.column_metas(table_id, column_ids)?;
660 } else {
661 self.ensure_table_state(table_id)?;
662 }
663
664 let mut tables = self.tables.write().unwrap();
665 if let Some(state) = tables.get_mut(&table_id) {
666 state.current.table_meta = None;
667 state.current.column_metas.clear();
668 state.current.constraints.clear();
669 state.current.constraint_names.clear();
670 state.current.single_indexes.clear();
671 state.current.multi_column_indexes.clear();
672 state.current.sort_indexes.clear();
673 state.current.triggers.clear();
674 }
675 drop(tables);
676 self.refresh_referencing_index_for_table(table_id);
677 Ok(())
678 }
679
680 pub fn remove_table_state(&self, table_id: TableId) {
682 self.tables.write().unwrap().remove(&table_id);
683 self.referencing_index
684 .write()
685 .unwrap()
686 .remove_child(table_id);
687 }
688
689 pub fn constraint_records(&self, table_id: TableId) -> LlkvResult<Vec<ConstraintRecord>> {
691 self.ensure_table_state(table_id)?;
692 let tables = self.tables.read().unwrap();
693 let state = tables.get(&table_id).unwrap();
694 Ok(state.current.constraints.values().cloned().collect())
695 }
696
697 pub fn constraint_records_by_id(
699 &self,
700 table_id: TableId,
701 constraint_ids: &[ConstraintId],
702 ) -> LlkvResult<Vec<Option<ConstraintRecord>>> {
703 self.ensure_table_state(table_id)?;
704 let tables = self.tables.read().unwrap();
705 let state = tables.get(&table_id).unwrap();
706 Ok(constraint_ids
707 .iter()
708 .map(|constraint_id| state.current.constraints.get(constraint_id).cloned())
709 .collect())
710 }
711
712 pub fn put_constraint_records(
714 &self,
715 table_id: TableId,
716 records: &[ConstraintRecord],
717 ) -> LlkvResult<()> {
718 self.ensure_table_state(table_id)?;
719 let mut tables = self.tables.write().unwrap();
720 let state = tables.get_mut(&table_id).unwrap();
721 for record in records {
722 state
723 .current
724 .constraints
725 .insert(record.constraint_id, record.clone());
726 }
727 drop(tables);
728 self.refresh_referencing_index_for_table(table_id);
729 Ok(())
730 }
731
732 pub fn put_constraint_names(
734 &self,
735 table_id: TableId,
736 names: &[(ConstraintId, Option<String>)],
737 ) -> LlkvResult<()> {
738 if names.is_empty() {
739 return Ok(());
740 }
741 self.ensure_table_state(table_id)?;
742 let mut tables = self.tables.write().unwrap();
743 if let Some(state) = tables.get_mut(&table_id) {
744 for (constraint_id, name) in names {
745 if let Some(name) = name {
746 state
747 .current
748 .constraint_names
749 .insert(*constraint_id, name.clone());
750 } else {
751 state.current.constraint_names.remove(constraint_id);
752 }
753 }
754 }
755 Ok(())
756 }
757
758 pub fn constraint_record_map(
760 &self,
761 table_id: TableId,
762 ) -> LlkvResult<FxHashMap<ConstraintId, ConstraintRecord>> {
763 self.ensure_table_state(table_id)?;
764 let tables = self.tables.read().unwrap();
765 let state = tables.get(&table_id).unwrap();
766 Ok(state.current.constraints.clone())
767 }
768
769 pub fn flush_table(&self, table_id: TableId) -> LlkvResult<()> {
771 self.ensure_table_state(table_id)?;
772 let mut tables = self.tables.write().unwrap();
773 let state = tables.get_mut(&table_id).unwrap();
774
775 let catalog = SysCatalog::new(&self.store);
776
777 match (
778 state.current.table_meta.as_ref(),
779 state.persisted.table_meta.as_ref(),
780 ) {
781 (Some(meta), Some(existing)) if meta != existing => {
782 catalog.put_table_meta(meta);
783 state.persisted.table_meta = Some(meta.clone());
784 }
785 (Some(meta), None) => {
786 catalog.put_table_meta(meta);
787 state.persisted.table_meta = Some(meta.clone());
788 }
789 (None, Some(_)) => {
790 catalog.delete_table_meta(table_id)?;
791 state.persisted.table_meta = None;
792 }
793 _ => {}
794 }
795
796 let mut dirty_columns: Vec<(FieldId, ColMeta)> = Vec::new();
797 for (field_id, meta) in &state.current.column_metas {
798 match state.persisted.column_metas.get(field_id) {
799 Some(existing) if existing == meta => {}
800 _ => dirty_columns.push((*field_id, meta.clone())),
801 }
802 }
803 for (field_id, meta) in dirty_columns.iter() {
804 catalog.put_col_meta(table_id, meta);
805 state.persisted.column_metas.insert(*field_id, meta.clone());
806 }
807
808 let removed_columns: Vec<FieldId> = state
809 .persisted
810 .column_metas
811 .keys()
812 .copied()
813 .filter(|field_id| !state.current.column_metas.contains_key(field_id))
814 .collect();
815 if !removed_columns.is_empty() {
816 catalog.delete_col_meta(table_id, &removed_columns)?;
817 for field_id in removed_columns {
818 state.persisted.column_metas.remove(&field_id);
819 }
820 }
821
822 let mut dirty_constraints: Vec<ConstraintRecord> = Vec::new();
823 for (constraint_id, record) in &state.current.constraints {
824 match state.persisted.constraints.get(constraint_id) {
825 Some(existing) if existing == record => {}
826 _ => dirty_constraints.push(record.clone()),
827 }
828 }
829 if !dirty_constraints.is_empty() {
830 catalog.put_constraint_records(table_id, &dirty_constraints)?;
831 for record in dirty_constraints {
832 state
833 .persisted
834 .constraints
835 .insert(record.constraint_id, record);
836 }
837 }
838
839 let removed_constraints: Vec<ConstraintId> = state
840 .persisted
841 .constraints
842 .keys()
843 .copied()
844 .filter(|constraint_id| !state.current.constraints.contains_key(constraint_id))
845 .collect();
846 if !removed_constraints.is_empty() {
847 catalog.delete_constraint_records(table_id, &removed_constraints)?;
848 for constraint_id in removed_constraints {
849 state.persisted.constraints.remove(&constraint_id);
850 }
851 }
852
853 let mut dirty_constraint_names: Vec<(ConstraintId, String)> = Vec::new();
854 for (constraint_id, name) in &state.current.constraint_names {
855 match state.persisted.constraint_names.get(constraint_id) {
856 Some(existing) if existing == name => {}
857 _ => dirty_constraint_names.push((*constraint_id, name.clone())),
858 }
859 }
860 if !dirty_constraint_names.is_empty() {
861 let records: Vec<ConstraintNameRecord> = dirty_constraint_names
862 .iter()
863 .map(|(constraint_id, name)| ConstraintNameRecord {
864 constraint_id: *constraint_id,
865 name: Some(name.clone()),
866 })
867 .collect();
868 catalog.put_constraint_names(table_id, &records)?;
869 for (constraint_id, name) in dirty_constraint_names {
870 state.persisted.constraint_names.insert(constraint_id, name);
871 }
872 }
873
874 let removed_constraint_names: Vec<ConstraintId> = state
875 .persisted
876 .constraint_names
877 .keys()
878 .copied()
879 .filter(|constraint_id| !state.current.constraint_names.contains_key(constraint_id))
880 .collect();
881 if !removed_constraint_names.is_empty() {
882 catalog.delete_constraint_names(table_id, &removed_constraint_names)?;
883 for constraint_id in removed_constraint_names {
884 state.persisted.constraint_names.remove(&constraint_id);
885 }
886 }
887
888 if state.current.multi_column_indexes != state.persisted.multi_column_indexes {
890 if state.current.multi_column_indexes.is_empty() {
891 catalog.delete_multi_column_indexes(table_id)?;
892 } else {
893 let mut entries: Vec<MultiColumnIndexEntryMeta> = state
894 .current
895 .multi_column_indexes
896 .values()
897 .cloned()
898 .collect();
899 entries.sort_by(|a, b| a.canonical_name.cmp(&b.canonical_name));
900 catalog.put_multi_column_indexes(table_id, &entries)?;
901 }
902 state.persisted.multi_column_indexes = state.current.multi_column_indexes.clone();
903 }
904
905 if state.current.single_indexes != state.persisted.single_indexes {
906 if state.current.single_indexes.is_empty() {
907 catalog.delete_single_column_indexes(table_id)?;
908 state.persisted.single_indexes.clear();
909 } else {
910 let mut entries: Vec<SingleColumnIndexEntryMeta> = state
911 .current
912 .single_indexes
913 .values()
914 .cloned()
915 .map(|entry| SingleColumnIndexEntryMeta {
916 index_name: entry.index_name,
917 canonical_name: entry.canonical_name,
918 column_id: entry.column_id,
919 column_name: entry.column_name,
920 unique: entry.unique,
921 ascending: entry.ascending,
922 nulls_first: entry.nulls_first,
923 })
924 .collect();
925 entries.sort_by(|a, b| a.canonical_name.cmp(&b.canonical_name));
926 catalog.put_single_column_indexes(table_id, &entries)?;
927 state.persisted.single_indexes = state.current.single_indexes.clone();
928 }
929 }
930
931 if state.current.triggers != state.persisted.triggers {
932 if state.current.triggers.is_empty() {
933 if !state.persisted.triggers.is_empty() {
934 catalog.delete_triggers(table_id)?;
935 }
936 state.persisted.triggers.clear();
937 } else {
938 let mut entries: Vec<TriggerEntryMeta> =
939 state.current.triggers.values().cloned().collect();
940 entries.sort_by(|a, b| a.canonical_name.cmp(&b.canonical_name));
941 catalog.put_triggers(table_id, &entries)?;
942 state.persisted.triggers = state.current.triggers.clone();
943 }
944 }
945
946 let sort_adds: Vec<FieldId> = state
947 .current
948 .sort_indexes
949 .iter()
950 .copied()
951 .filter(|field_id| !state.persisted.sort_indexes.contains(field_id))
952 .collect();
953 let sort_removes: Vec<FieldId> = state
954 .persisted
955 .sort_indexes
956 .iter()
957 .copied()
958 .filter(|field_id| !state.current.sort_indexes.contains(field_id))
959 .collect();
960 if !sort_adds.is_empty() || !sort_removes.is_empty() {
961 let table = Table::from_id_and_store(table_id, Arc::clone(&self.store))?;
962 for field_id in &sort_adds {
963 table.register_sort_index(*field_id)?;
964 state.persisted.sort_indexes.insert(*field_id);
965 }
966 for field_id in &sort_removes {
967 table.unregister_sort_index(*field_id)?;
968 state.persisted.sort_indexes.remove(field_id);
969 }
970 }
971
972 Ok(())
973 }
974
975 pub fn flush_all(&self) -> LlkvResult<()> {
977 let table_ids: Vec<TableId> = {
978 let tables = self.tables.read().unwrap();
979 tables.keys().copied().collect()
980 };
981 for table_id in table_ids {
982 self.flush_table(table_id)?;
983 }
984 Ok(())
985 }
986
987 pub fn all_table_metas(&self) -> LlkvResult<Vec<(TableId, TableMeta)>> {
989 let catalog = SysCatalog::new(&self.store);
990 catalog.all_table_metas()
991 }
992
993 pub fn all_multi_column_unique_metas(&self) -> LlkvResult<Vec<TableMultiColumnIndexMeta>> {
995 let catalog = SysCatalog::new(&self.store);
996 let all = catalog.all_multi_column_index_metas()?;
997 Ok(all
999 .into_iter()
1000 .map(|mut meta| {
1001 meta.indexes.retain(|idx| idx.unique);
1002 meta
1003 })
1004 .filter(|meta| !meta.indexes.is_empty())
1005 .collect())
1006 }
1007
1008 pub fn foreign_key_descriptors(
1010 &self,
1011 table_id: TableId,
1012 ) -> LlkvResult<Vec<ForeignKeyDescriptor>> {
1013 let records = self.constraint_records(table_id)?;
1014 let mut descriptors = Vec::new();
1015
1016 for record in records {
1017 if !record.is_active() {
1018 continue;
1019 }
1020
1021 let ConstraintKind::ForeignKey(fk) = record.kind else {
1022 continue;
1023 };
1024
1025 descriptors.push(ForeignKeyDescriptor {
1026 constraint_id: record.constraint_id,
1027 referencing_table_id: table_id,
1028 referencing_field_ids: fk.referencing_field_ids.clone(),
1029 referenced_table_id: fk.referenced_table,
1030 referenced_field_ids: fk.referenced_field_ids.clone(),
1031 on_delete: fk.on_delete,
1032 on_update: fk.on_update,
1033 });
1034 }
1035
1036 Ok(descriptors)
1037 }
1038
1039 pub fn foreign_key_views(
1041 &self,
1042 catalog: &TableCatalog,
1043 table_id: TableId,
1044 ) -> LlkvResult<Vec<ForeignKeyView>> {
1045 let descriptors = self.foreign_key_descriptors(table_id)?;
1046
1047 if descriptors.is_empty() {
1048 return Ok(Vec::new());
1049 }
1050
1051 let (referencing_display, referencing_canonical) =
1052 resolve_table_name(catalog, self, table_id)?;
1053
1054 let mut details = Vec::with_capacity(descriptors.len());
1055 for descriptor in descriptors {
1056 let referenced_table_id = descriptor.referenced_table_id;
1057 let (referenced_display, referenced_canonical) =
1058 resolve_table_name(catalog, self, referenced_table_id)?;
1059
1060 let referencing_column_names =
1061 self.column_names(table_id, &descriptor.referencing_field_ids)?;
1062 let referenced_column_names =
1063 self.column_names(referenced_table_id, &descriptor.referenced_field_ids)?;
1064 let constraint_name = self.constraint_name_for(table_id, descriptor.constraint_id)?;
1065
1066 details.push(ForeignKeyView {
1067 constraint_id: descriptor.constraint_id,
1068 constraint_name,
1069 referencing_table_id: descriptor.referencing_table_id,
1070 referencing_table_display: referencing_display.clone(),
1071 referencing_table_canonical: referencing_canonical.clone(),
1072 referencing_field_ids: descriptor.referencing_field_ids.clone(),
1073 referencing_column_names,
1074 referenced_table_id,
1075 referenced_table_display: referenced_display.clone(),
1076 referenced_table_canonical: referenced_canonical.clone(),
1077 referenced_field_ids: descriptor.referenced_field_ids.clone(),
1078 referenced_column_names,
1079 on_delete: descriptor.on_delete,
1080 on_update: descriptor.on_update,
1081 });
1082 }
1083
1084 Ok(details)
1085 }
1086
1087 pub fn table_view(
1089 &self,
1090 catalog: &TableCatalog,
1091 table_id: TableId,
1092 field_ids: &[FieldId],
1093 ) -> LlkvResult<TableView> {
1094 let table_meta = self.table_meta(table_id)?;
1095 let column_metas = self.column_metas(table_id, field_ids)?;
1096 let constraint_records = self.constraint_records(table_id)?;
1097 let multi_column_uniques = self.multi_column_uniques(table_id)?;
1098 let foreign_keys = self.foreign_key_views(catalog, table_id)?;
1099
1100 Ok(TableView {
1101 table_meta,
1102 column_metas,
1103 constraint_records,
1104 multi_column_uniques,
1105 foreign_keys,
1106 })
1107 }
1108
1109 pub fn validate_and_register_foreign_keys<F>(
1111 &self,
1112 referencing_table: &ForeignKeyTableInfo,
1113 specs: &[ForeignKeySpec],
1114 lookup_table: F,
1115 timestamp_micros: u64,
1116 ) -> LlkvResult<Vec<ValidatedForeignKey>>
1117 where
1118 F: FnMut(&str) -> LlkvResult<ForeignKeyTableInfo>,
1119 {
1120 let validated = validate_foreign_keys(referencing_table, specs, lookup_table)?;
1121 self.register_foreign_keys(referencing_table.table_id, &validated, timestamp_micros)?;
1122 Ok(validated)
1123 }
1124
1125 pub fn register_foreign_keys(
1127 &self,
1128 table_id: TableId,
1129 foreign_keys: &[ValidatedForeignKey],
1130 timestamp_micros: u64,
1131 ) -> LlkvResult<()> {
1132 if foreign_keys.is_empty() {
1133 return Ok(());
1134 }
1135
1136 let existing_constraints = self.constraint_record_map(table_id)?;
1137 let mut next_constraint_id = existing_constraints
1138 .keys()
1139 .copied()
1140 .max()
1141 .unwrap_or(0)
1142 .saturating_add(1);
1143
1144 let mut constraint_records = Vec::with_capacity(foreign_keys.len());
1145 let mut constraint_names: Vec<(ConstraintId, Option<String>)> =
1146 Vec::with_capacity(foreign_keys.len());
1147
1148 for fk in foreign_keys {
1149 let constraint_id = next_constraint_id;
1150 constraint_records.push(ConstraintRecord {
1151 constraint_id,
1152 kind: ConstraintKind::ForeignKey(ForeignKeyConstraint {
1153 referencing_field_ids: fk.referencing_field_ids.clone(),
1154 referenced_table: fk.referenced_table_id,
1155 referenced_field_ids: fk.referenced_field_ids.clone(),
1156 on_delete: fk.on_delete,
1157 on_update: fk.on_update,
1158 }),
1159 state: ConstraintState::Active,
1160 revision: 1,
1161 last_modified_micros: timestamp_micros,
1162 });
1163 constraint_names.push((constraint_id, fk.name.clone()));
1164 next_constraint_id = next_constraint_id.saturating_add(1);
1165 }
1166
1167 self.put_constraint_records(table_id, &constraint_records)?;
1168 self.put_constraint_names(table_id, &constraint_names)?;
1169 self.flush_table(table_id)?;
1170
1171 Ok(())
1172 }
1173
1174 pub fn apply_column_definitions(
1176 &self,
1177 table_id: TableId,
1178 columns: &[TableColumn],
1179 timestamp_micros: u64,
1180 ) -> LlkvResult<()> {
1181 if columns.is_empty() {
1182 return Ok(());
1183 }
1184
1185 self.ensure_table_state(table_id)?;
1186
1187 for column in columns {
1188 let column_meta = ColMeta {
1189 col_id: column.field_id,
1190 name: Some(column.name.clone()),
1191 flags: 0,
1192 default: None,
1193 };
1194 self.set_column_meta(table_id, column_meta)?;
1195 }
1196
1197 let table = Table::from_id_and_store(table_id, Arc::clone(&self.store))?;
1198 let store = table.store();
1199
1200 for column in columns {
1201 let logical_field_id = LogicalFieldId::for_user(table_id, column.field_id);
1202 store.ensure_column_registered(logical_field_id, &column.data_type)?;
1203 store.data_type(logical_field_id)?;
1204 }
1205
1206 let created_by_lfid = LogicalFieldId::for_mvcc_created_by(table_id);
1207 store.ensure_column_registered(created_by_lfid, &DataType::UInt64)?;
1208
1209 let deleted_by_lfid = LogicalFieldId::for_mvcc_deleted_by(table_id);
1210 store.ensure_column_registered(deleted_by_lfid, &DataType::UInt64)?;
1211
1212 let existing = self.constraint_record_map(table_id)?;
1213 let mut next_constraint_id = existing
1214 .keys()
1215 .copied()
1216 .max()
1217 .unwrap_or(0)
1218 .saturating_add(1);
1219
1220 let mut constraints = Vec::new();
1221
1222 let primary_key_fields: Vec<FieldId> = columns
1223 .iter()
1224 .filter(|col| col.primary_key)
1225 .map(|col| col.field_id)
1226 .collect();
1227 if !primary_key_fields.is_empty() {
1228 constraints.push(ConstraintRecord {
1229 constraint_id: next_constraint_id,
1230 kind: ConstraintKind::PrimaryKey(PrimaryKeyConstraint {
1231 field_ids: primary_key_fields,
1232 }),
1233 state: ConstraintState::Active,
1234 revision: 1,
1235 last_modified_micros: timestamp_micros,
1236 });
1237 next_constraint_id = next_constraint_id.saturating_add(1);
1238 }
1239
1240 for column in columns.iter().filter(|col| col.unique && !col.primary_key) {
1241 constraints.push(ConstraintRecord {
1242 constraint_id: next_constraint_id,
1243 kind: ConstraintKind::Unique(UniqueConstraint {
1244 field_ids: vec![column.field_id],
1245 }),
1246 state: ConstraintState::Active,
1247 revision: 1,
1248 last_modified_micros: timestamp_micros,
1249 });
1250 next_constraint_id = next_constraint_id.saturating_add(1);
1251 }
1252
1253 if !constraints.is_empty() {
1254 self.put_constraint_records(table_id, &constraints)?;
1255 }
1256
1257 Ok(())
1258 }
1259
1260 pub fn column_data_type(&self, table_id: TableId, field_id: FieldId) -> LlkvResult<DataType> {
1261 let table = Table::from_id_and_store(table_id, Arc::clone(&self.store))?;
1262 let store = table.store();
1263 let logical_field_id = LogicalFieldId::for_user(table_id, field_id);
1264 store.data_type(logical_field_id)
1265 }
1266
1267 pub fn register_multi_column_unique(
1269 &self,
1270 table_id: TableId,
1271 column_ids: &[FieldId],
1272 index_name: Option<String>,
1273 ) -> LlkvResult<MultiColumnUniqueRegistration> {
1274 let mut created = false;
1275 let mut existing_name: Option<Option<String>> = None;
1276 let column_vec: Vec<FieldId> = column_ids.to_vec();
1277
1278 let canonical_name = format!(
1280 "__unique_{}_{}",
1281 table_id,
1282 column_vec
1283 .iter()
1284 .map(|id| id.to_string())
1285 .collect::<Vec<_>>()
1286 .join("_")
1287 );
1288
1289 self.update_multi_column_uniques(table_id, |entries| {
1290 if let Some(existing) = entries.iter().find(|entry| entry.column_ids == column_vec) {
1291 existing_name = Some(existing.index_name.clone());
1292 } else {
1293 entries.push(MultiColumnIndexEntryMeta {
1294 index_name: index_name.clone(),
1295 canonical_name: canonical_name.clone(),
1296 column_ids: column_vec.clone(),
1297 unique: true,
1298 });
1299 created = true;
1300 }
1301 })?;
1302
1303 if created {
1304 Ok(MultiColumnUniqueRegistration::Created)
1305 } else {
1306 Ok(MultiColumnUniqueRegistration::AlreadyExists {
1307 index_name: existing_name.unwrap_or(None),
1308 })
1309 }
1310 }
1311
1312 fn column_names(&self, table_id: TableId, field_ids: &[FieldId]) -> LlkvResult<Vec<String>> {
1313 if field_ids.is_empty() {
1314 return Ok(Vec::new());
1315 }
1316
1317 let metas = self.column_metas(table_id, field_ids)?;
1318 let mut names = Vec::with_capacity(field_ids.len());
1319 for (idx, field_id) in field_ids.iter().enumerate() {
1320 let name = metas
1321 .get(idx)
1322 .and_then(|meta| meta.as_ref())
1323 .and_then(|meta| meta.name.clone())
1324 .unwrap_or_else(|| format!("col_{}", field_id));
1325 names.push(name);
1326 }
1327 Ok(names)
1328 }
1329
1330 pub fn reserve_table_id(&self) -> LlkvResult<TableId> {
1332 let catalog = SysCatalog::new(&self.store);
1333
1334 let mut next = match catalog.get_next_table_id()? {
1335 Some(value) => value,
1336 None => {
1337 let seed = catalog
1338 .max_table_id()?
1339 .unwrap_or(reserved::CATALOG_TABLE_ID);
1340 let initial = seed.checked_add(1).ok_or_else(|| {
1341 Error::InvalidArgumentError("exhausted available table ids".into())
1342 })?;
1343 catalog.put_next_table_id(initial)?;
1344 initial
1345 }
1346 };
1347
1348 while reserved::is_reserved_table_id(next) {
1349 next = next.checked_add(1).ok_or_else(|| {
1350 Error::InvalidArgumentError("exhausted available table ids".into())
1351 })?;
1352 }
1353
1354 let mut following = next
1355 .checked_add(1)
1356 .ok_or_else(|| Error::InvalidArgumentError("exhausted available table ids".into()))?;
1357
1358 while reserved::is_reserved_table_id(following) {
1359 following = following.checked_add(1).ok_or_else(|| {
1360 Error::InvalidArgumentError("exhausted available table ids".into())
1361 })?;
1362 }
1363
1364 catalog.put_next_table_id(following)?;
1365 Ok(next)
1366 }
1367
1368 pub fn ensure_next_table_id_at_least(&self, minimum: TableId) -> LlkvResult<()> {
1375 if reserved::is_reserved_table_id(minimum) {
1376 return Err(Error::InvalidArgumentError(
1377 reserved::reserved_table_id_message(minimum),
1378 ));
1379 }
1380
1381 let catalog = SysCatalog::new(&self.store);
1382 match catalog.get_next_table_id()? {
1383 Some(current) if current >= minimum => Ok(()),
1384 _ => catalog.put_next_table_id(minimum),
1385 }
1386 }
1387
1388 fn field_has_sort_index(&self, table_id: TableId, field_id: FieldId) -> LlkvResult<bool> {
1395 let table = Table::from_id_and_store(table_id, Arc::clone(&self.store))?;
1396 let indexes = table.list_registered_indexes(field_id)?;
1397 Ok(indexes.contains(&IndexKind::Sort))
1398 }
1399}
1400
1401#[cfg(test)]
1402mod tests {
1403 use super::*;
1404 use crate::constraints::{ConstraintKind, ConstraintState, PrimaryKeyConstraint};
1405 use crate::{MultiColumnIndexEntryMeta, Table};
1406 use llkv_column_map::ColumnStore;
1407 use llkv_column_map::store::IndexKind;
1408 use llkv_storage::pager::MemPager;
1409 use std::sync::Arc;
1410
1411 #[test]
1412 fn metadata_manager_persists_and_loads() {
1413 let pager = Arc::new(MemPager::default());
1414 let store = Arc::new(ColumnStore::open(Arc::clone(&pager)).unwrap());
1415 let manager = MetadataManager::new(Arc::clone(&store));
1416
1417 let table_id: TableId = 42;
1418 let table_meta = TableMeta {
1419 table_id,
1420 name: Some("users".into()),
1421 created_at_micros: 123,
1422 flags: 0,
1423 epoch: 1,
1424 view_definition: None,
1425 };
1426 manager
1427 .set_table_meta(table_id, table_meta.clone())
1428 .unwrap();
1429
1430 {
1431 let tables = manager.tables.read().unwrap();
1432 let state = tables.get(&table_id).unwrap();
1433 assert!(state.current.table_meta.is_some());
1434 }
1435
1436 let column_meta = ColMeta {
1437 col_id: 1,
1438 name: Some("id".into()),
1439 flags: 0,
1440 default: None,
1441 };
1442 manager
1443 .set_column_meta(table_id, column_meta.clone())
1444 .unwrap();
1445
1446 let logical_field_id =
1447 llkv_column_map::types::LogicalFieldId::for_user(table_id, column_meta.col_id);
1448 store
1449 .ensure_column_registered(logical_field_id, &arrow::datatypes::DataType::Utf8)
1450 .unwrap();
1451
1452 manager
1453 .register_sort_index(table_id, column_meta.col_id)
1454 .unwrap();
1455
1456 let constraint = ConstraintRecord {
1457 constraint_id: 7,
1458 kind: ConstraintKind::PrimaryKey(PrimaryKeyConstraint {
1459 field_ids: vec![column_meta.col_id],
1460 }),
1461 state: ConstraintState::Active,
1462 revision: 1,
1463 last_modified_micros: 456,
1464 };
1465 manager
1466 .put_constraint_records(table_id, std::slice::from_ref(&constraint))
1467 .unwrap();
1468
1469 let multi_unique = MultiColumnIndexEntryMeta {
1470 index_name: Some("uniq_users_name".into()),
1471 canonical_name: "uniq_users_name".to_lowercase(),
1472 column_ids: vec![column_meta.col_id],
1473 unique: true,
1474 };
1475 manager
1476 .set_multi_column_uniques(table_id, vec![multi_unique.clone()])
1477 .unwrap();
1478
1479 assert_eq!(
1480 manager.table_meta(table_id).unwrap(),
1481 Some(table_meta.clone())
1482 );
1483
1484 manager.flush_table(table_id).unwrap();
1485
1486 let table = Table::from_id_and_store(table_id, Arc::clone(&store)).unwrap();
1487 let indexes = table.list_registered_indexes(column_meta.col_id).unwrap();
1488 assert!(indexes.contains(&IndexKind::Sort));
1489
1490 let verify_catalog = SysCatalog::new(&store);
1491 let column_roundtrip = verify_catalog.get_cols_meta(table_id, &[column_meta.col_id]);
1492 assert_eq!(column_roundtrip[0].as_ref(), Some(&column_meta));
1493 let constraints = verify_catalog
1494 .constraint_records_for_table(table_id)
1495 .unwrap();
1496 assert_eq!(constraints, vec![constraint.clone()]);
1497 let unique_roundtrip = verify_catalog.get_multi_column_indexes(table_id).unwrap();
1498 assert_eq!(unique_roundtrip, vec![multi_unique.clone()]);
1499
1500 let meta_from_cache = manager.table_meta(table_id).unwrap();
1501 assert_eq!(meta_from_cache, Some(table_meta.clone()));
1502
1503 let columns_from_cache = manager
1504 .column_metas(table_id, &[column_meta.col_id])
1505 .unwrap();
1506 assert_eq!(columns_from_cache[0].as_ref(), Some(&column_meta));
1507
1508 let constraints_from_cache = manager.constraint_records(table_id).unwrap();
1509 assert_eq!(constraints_from_cache, vec![constraint.clone()]);
1510
1511 let uniques_from_cache = manager.multi_column_uniques(table_id).unwrap();
1512 assert_eq!(uniques_from_cache, vec![multi_unique]);
1513
1514 manager.flush_table(table_id).unwrap();
1516 }
1517
1518 #[test]
1519 fn metadata_manager_lazy_loads_columns_and_constraints() {
1520 let pager = Arc::new(MemPager::default());
1521 let store = Arc::new(ColumnStore::open(Arc::clone(&pager)).unwrap());
1522 let manager = MetadataManager::new(Arc::clone(&store));
1523
1524 let table_id: TableId = 99;
1525 let column_meta = ColMeta {
1526 col_id: 3,
1527 name: Some("value".into()),
1528 flags: 0,
1529 default: None,
1530 };
1531 let initial_catalog = SysCatalog::new(&store);
1532 initial_catalog.put_col_meta(table_id, &column_meta);
1533
1534 let constraint = ConstraintRecord {
1535 constraint_id: 15,
1536 kind: ConstraintKind::PrimaryKey(PrimaryKeyConstraint {
1537 field_ids: vec![column_meta.col_id],
1538 }),
1539 state: ConstraintState::Active,
1540 revision: 1,
1541 last_modified_micros: 0,
1542 };
1543 initial_catalog
1544 .put_constraint_records(table_id, std::slice::from_ref(&constraint))
1545 .unwrap();
1546 let multi_unique = MultiColumnIndexEntryMeta {
1547 index_name: Some("uniq_value".into()),
1548 canonical_name: "uniq_value".to_lowercase(),
1549 column_ids: vec![column_meta.col_id],
1550 unique: true,
1551 };
1552 initial_catalog
1553 .put_multi_column_indexes(table_id, std::slice::from_ref(&multi_unique))
1554 .unwrap();
1555
1556 let columns = manager
1557 .column_metas(table_id, &[column_meta.col_id])
1558 .unwrap();
1559 assert_eq!(columns[0].as_ref(), Some(&column_meta));
1560
1561 let constraints = manager.constraint_records(table_id).unwrap();
1562 assert_eq!(constraints, vec![constraint]);
1563
1564 let uniques = manager.multi_column_uniques(table_id).unwrap();
1565 assert_eq!(uniques, vec![multi_unique]);
1566 }
1567}
1568
1569#[derive(Clone, Debug)]
1571pub struct ForeignKeyDescriptor {
1572 pub constraint_id: ConstraintId,
1573 pub referencing_table_id: TableId,
1574 pub referencing_field_ids: Vec<FieldId>,
1575 pub referenced_table_id: TableId,
1576 pub referenced_field_ids: Vec<FieldId>,
1577 pub on_delete: ForeignKeyAction,
1578 pub on_update: ForeignKeyAction,
1579}
1580
1581#[derive(Debug, Clone, PartialEq, Eq)]
1583pub enum MultiColumnUniqueRegistration {
1584 Created,
1585 AlreadyExists { index_name: Option<String> },
1586}