1use itertools::Itertools;
10use spacetimedb_lib::db::auth::{StAccess, StTableType};
11use spacetimedb_lib::db::error::{DefType, SchemaError};
12use spacetimedb_lib::db::raw_def::v9::RawSql;
13use spacetimedb_lib::db::raw_def::{generate_cols_name, RawConstraintDefV8};
14use spacetimedb_lib::relation::{combine_constraints, Column, DbTable, FieldName, Header};
15use spacetimedb_lib::{AlgebraicType, ProductType, ProductTypeElement};
16use spacetimedb_primitives::*;
17use spacetimedb_sats::product_value::InvalidFieldError;
18use spacetimedb_sats::WithTypespace;
19use std::collections::BTreeMap;
20use std::sync::Arc;
21
22use crate::def::{
23 ColumnDef, ConstraintData, ConstraintDef, IndexAlgorithm, IndexDef, ModuleDef, ModuleDefLookup, ScheduleDef,
24 SequenceDef, TableDef, UniqueConstraintData,
25};
26use crate::identifier::Identifier;
27
28pub trait Schema: Sized {
30 type Def: ModuleDefLookup;
32 type Id;
34 type ParentId;
37
38 fn from_module_def(module_def: &ModuleDef, def: &Self::Def, parent_id: Self::ParentId, id: Self::Id) -> Self;
48
49 fn check_compatible(&self, module_def: &ModuleDef, def: &Self::Def) -> Result<(), anyhow::Error>;
51}
52
53#[derive(Debug, Clone, PartialEq, Eq)]
58pub struct TableSchema {
59 pub table_id: TableId,
61
62 pub table_name: Box<str>,
64
65 columns: Vec<ColumnSchema>,
69
70 pub primary_key: Option<ColId>,
77
78 pub indexes: Vec<IndexSchema>,
80
81 pub constraints: Vec<ConstraintSchema>,
83
84 pub sequences: Vec<SequenceSchema>,
86
87 pub table_type: StTableType,
89
90 pub table_access: StAccess,
92
93 pub schedule: Option<ScheduleSchema>,
95
96 row_type: ProductType,
98}
99
100impl TableSchema {
101 #[allow(clippy::too_many_arguments)]
103 pub fn new(
104 table_id: TableId,
105 table_name: Box<str>,
106 columns: Vec<ColumnSchema>,
107 indexes: Vec<IndexSchema>,
108 constraints: Vec<ConstraintSchema>,
109 sequences: Vec<SequenceSchema>,
110 table_type: StTableType,
111 table_access: StAccess,
112 schedule: Option<ScheduleSchema>,
113 primary_key: Option<ColId>,
114 ) -> Self {
115 let row_type = ProductType::new(
116 columns
117 .iter()
118 .map(|c| ProductTypeElement {
119 name: Some(c.col_name.clone()),
120 algebraic_type: c.col_type.clone(),
121 })
122 .collect(),
123 );
124
125 Self {
126 table_id,
127 table_name,
128 columns,
129 indexes,
130 constraints,
131 sequences,
132 table_type,
133 table_access,
134 row_type,
135 schedule,
136 primary_key,
137 }
138 }
139
140 #[cfg(feature = "test")]
143 pub fn from_product_type(ty: ProductType) -> TableSchema {
144 let columns = ty
145 .elements
146 .iter()
147 .enumerate()
148 .map(|(col_pos, element)| ColumnSchema {
149 table_id: TableId::SENTINEL,
150 col_pos: ColId(col_pos as _),
151 col_name: element.name.clone().unwrap_or_else(|| format!("col{}", col_pos).into()),
152 col_type: element.algebraic_type.clone(),
153 })
154 .collect();
155
156 TableSchema::new(
157 TableId::SENTINEL,
158 "TestTable".into(),
159 columns,
160 vec![],
161 vec![],
162 vec![],
163 StTableType::User,
164 StAccess::Public,
165 None,
166 None,
167 )
168 }
169
170 pub fn update_table_id(&mut self, id: TableId) {
173 self.table_id = id;
174 self.columns.iter_mut().for_each(|c| c.table_id = id);
175 self.indexes.iter_mut().for_each(|i| i.table_id = id);
176 self.constraints.iter_mut().for_each(|c| c.table_id = id);
177 self.sequences.iter_mut().for_each(|s| s.table_id = id);
178 if let Some(s) = self.schedule.as_mut() {
179 s.table_id = id;
180 }
181 }
182
183 pub fn into_columns(self) -> Vec<ColumnSchema> {
185 self.columns
186 }
187
188 pub fn columns(&self) -> &[ColumnSchema] {
191 &self.columns
192 }
193
194 pub fn clear_adjacent_schemas(&mut self) {
196 self.indexes.clear();
197 self.sequences.clear();
198 self.constraints.clear();
199 }
200
201 pub fn update_sequence(&mut self, of: SequenceSchema) {
205 if let Some(x) = self.sequences.iter_mut().find(|x| x.sequence_id == of.sequence_id) {
206 *x = of;
207 } else {
208 self.sequences.push(of);
209 }
210 }
211
212 pub fn remove_sequence(&mut self, sequence_id: SequenceId) {
214 self.sequences.retain(|x| x.sequence_id != sequence_id)
215 }
216
217 pub fn update_index(&mut self, of: IndexSchema) {
219 if let Some(x) = self.indexes.iter_mut().find(|x| x.index_id == of.index_id) {
220 *x = of;
221 } else {
222 self.indexes.push(of);
223 }
224 }
225
226 pub fn remove_index(&mut self, index_id: IndexId) {
228 self.indexes.retain(|x| x.index_id != index_id)
229 }
230
231 pub fn update_constraint(&mut self, of: ConstraintSchema) {
233 if let Some(x) = self
234 .constraints
235 .iter_mut()
236 .find(|x| x.constraint_id == of.constraint_id)
237 {
238 *x = of;
239 } else {
240 self.constraints.push(of);
241 }
242 }
243
244 pub fn remove_constraint(&mut self, constraint_id: ConstraintId) {
246 self.constraints.retain(|x| x.constraint_id != constraint_id)
247 }
248
249 pub fn generate_cols_name(&self, columns: &ColList) -> String {
255 generate_cols_name(columns, |p| self.get_column(p.idx()).map(|c| &*c.col_name))
256 }
257
258 pub fn get_column_by_field(&self, field: FieldName) -> Option<&ColumnSchema> {
264 self.get_column(field.col.idx())
265 }
266
267 pub fn get_columns(&self, columns: &ColList) -> Vec<(ColId, Option<&ColumnSchema>)> {
270 columns.iter().map(|col| (col, self.columns.get(col.idx()))).collect()
271 }
272
273 pub fn get_column(&self, pos: usize) -> Option<&ColumnSchema> {
275 self.columns.get(pos)
276 }
277
278 pub fn get_column_by_name(&self, col_name: &str) -> Option<&ColumnSchema> {
280 self.columns.iter().find(|x| &*x.col_name == col_name)
281 }
282
283 pub fn get_column_id_by_name(&self, col_name: &str) -> Option<ColId> {
287 self.columns
288 .iter()
289 .position(|x| &*x.col_name == col_name)
290 .map(|x| x.into())
291 }
292
293 pub fn project(&self, indexes: impl Iterator<Item = ColId>) -> Result<Vec<&ColumnSchema>, InvalidFieldError> {
295 indexes
296 .map(|index| self.get_column(index.0 as usize).ok_or_else(|| index.into()))
297 .collect()
298 }
299
300 pub fn project_not_empty(&self, indexes: ColList) -> Result<Vec<&ColumnSchema>, InvalidFieldError> {
303 self.project(indexes.iter())
304 }
305
306 pub fn get_row_type(&self) -> &ProductType {
308 &self.row_type
309 }
310
311 pub fn into_row_type(self) -> ProductType {
313 self.row_type
314 }
315
316 fn backcompat_constraints_iter(&self) -> impl Iterator<Item = (ColList, Constraints)> + '_ {
318 self.constraints
319 .iter()
320 .map(|x| -> (ColList, Constraints) {
321 match &x.data {
322 ConstraintData::Unique(unique) => (unique.columns.clone().into(), Constraints::unique()),
323 }
324 })
325 .chain(self.indexes.iter().map(|x| match &x.index_algorithm {
326 IndexAlgorithm::BTree(btree) => (btree.columns.clone(), Constraints::indexed()),
327 }))
328 .chain(
329 self.sequences
330 .iter()
331 .map(|x| (col_list![x.col_pos], Constraints::auto_inc())),
332 )
333 .chain(
334 self.primary_key
335 .iter()
336 .map(|x| (col_list![*x], Constraints::primary_key())),
337 )
338 }
339
340 pub fn backcompat_constraints(&self) -> BTreeMap<ColList, Constraints> {
344 combine_constraints(self.backcompat_constraints_iter())
345 }
346
347 pub fn backcompat_column_constraints(&self) -> BTreeMap<ColList, Constraints> {
352 let mut result = combine_constraints(self.backcompat_constraints_iter());
353 for col in &self.columns {
354 result.entry(col_list![col.col_pos]).or_insert(Constraints::unset());
355 }
356 result
357 }
358
359 pub fn pk(&self) -> Option<&ColumnSchema> {
361 self.primary_key.and_then(|pk| self.get_column(pk.0 as usize))
362 }
363
364 pub fn validated(self) -> Result<Self, Vec<SchemaError>> {
373 let mut errors = Vec::new();
374
375 if self.table_name.is_empty() {
376 errors.push(SchemaError::EmptyTableName {
377 table_id: self.table_id,
378 });
379 }
380
381 let columns_not_found = self
382 .sequences
383 .iter()
384 .map(|x| (DefType::Sequence, x.sequence_name.clone(), ColList::new(x.col_pos)))
385 .chain(self.indexes.iter().map(|x| match &x.index_algorithm {
386 IndexAlgorithm::BTree(btree) => (DefType::Index, x.index_name.clone(), btree.columns.clone()),
387 }))
388 .chain(self.constraints.iter().map(|x| {
389 (
390 DefType::Constraint,
391 x.constraint_name.clone(),
392 match &x.data {
393 ConstraintData::Unique(unique) => unique.columns.clone().into(),
394 },
395 )
396 }))
397 .filter_map(|(ty, name, cols)| {
398 let empty: Vec<_> = self
399 .get_columns(&cols)
400 .iter()
401 .filter_map(|(col, x)| if x.is_none() { Some(*col) } else { None })
402 .collect();
403
404 if empty.is_empty() {
405 None
406 } else {
407 Some(SchemaError::ColumnsNotFound {
408 name,
409 table: self.table_name.clone(),
410 columns: empty,
411 ty,
412 })
413 }
414 });
415
416 errors.extend(columns_not_found);
417
418 errors.extend(self.columns.iter().filter_map(|x| {
419 if x.col_name.is_empty() {
420 Some(SchemaError::EmptyName {
421 table: self.table_name.clone(),
422 ty: DefType::Column,
423 id: x.col_pos.0 as _,
424 })
425 } else {
426 None
427 }
428 }));
429
430 errors.extend(self.indexes.iter().filter_map(|x| {
431 if x.index_name.is_empty() {
432 Some(SchemaError::EmptyName {
433 table: self.table_name.clone(),
434 ty: DefType::Index,
435 id: x.index_id.0,
436 })
437 } else {
438 None
439 }
440 }));
441 errors.extend(self.constraints.iter().filter_map(|x| {
442 if x.constraint_name.is_empty() {
443 Some(SchemaError::EmptyName {
444 table: self.table_name.clone(),
445 ty: DefType::Constraint,
446 id: x.constraint_id.0,
447 })
448 } else {
449 None
450 }
451 }));
452
453 errors.extend(self.sequences.iter().filter_map(|x| {
454 if x.sequence_name.is_empty() {
455 Some(SchemaError::EmptyName {
456 table: self.table_name.clone(),
457 ty: DefType::Sequence,
458 id: x.sequence_id.0,
459 })
460 } else {
461 None
462 }
463 }));
464
465 if let Some(err) = self
467 .sequences
468 .iter()
469 .group_by(|&seq| seq.col_pos)
470 .into_iter()
471 .find_map(|(key, group)| {
472 let count = group.count();
473 if count > 1 {
474 Some(SchemaError::OneAutoInc {
475 table: self.table_name.clone(),
476 field: self.columns[key.idx()].col_name.clone(),
477 })
478 } else {
479 None
480 }
481 })
482 {
483 errors.push(err);
484 }
485
486 if errors.is_empty() {
487 Ok(self)
488 } else {
489 Err(errors)
490 }
491 }
492
493 pub fn janky_fix_column_defs(&mut self, module_def: &ModuleDef) {
497 let table_name = Identifier::new(self.table_name.clone()).unwrap();
498 for col in &mut self.columns {
499 let def: &ColumnDef = module_def
500 .lookup((&table_name, &Identifier::new(col.col_name.clone()).unwrap()))
501 .unwrap();
502 col.col_type = def.ty.clone();
503 }
504 let table_def: &TableDef = module_def.expect_lookup(&table_name);
505 self.row_type = module_def.typespace()[table_def.product_type_ref]
506 .as_product()
507 .unwrap()
508 .clone();
509 }
510
511 pub fn normalize(&mut self) {
515 self.indexes.sort_by(|a, b| a.index_name.cmp(&b.index_name));
516 self.constraints
517 .sort_by(|a, b| a.constraint_name.cmp(&b.constraint_name));
518 self.sequences.sort_by(|a, b| a.sequence_name.cmp(&b.sequence_name));
519 }
520}
521
522macro_rules! ensure_eq {
524 ($a:expr, $b:expr, $msg:expr) => {
525 if $a != $b {
526 anyhow::bail!(
527 "{0}: expected {1} == {2}:\n {1}: {3:?}\n {2}: {4:?}",
528 $msg,
529 stringify!($a),
530 stringify!($b),
531 $a,
532 $b
533 );
534 }
535 };
536}
537
538impl Schema for TableSchema {
539 type Def = TableDef;
540 type Id = TableId;
541 type ParentId = ();
542
543 fn from_module_def(
545 module_def: &ModuleDef,
546 def: &Self::Def,
547 _parent_id: Self::ParentId,
548 table_id: Self::Id,
549 ) -> Self {
550 module_def.expect_contains(def);
551
552 let TableDef {
553 name,
554 product_type_ref: _,
555 primary_key,
556 columns,
557 indexes,
558 constraints,
559 sequences,
560 schedule,
561 table_type,
562 table_access,
563 } = def;
564
565 let columns: Vec<ColumnSchema> = columns
566 .iter()
567 .enumerate()
568 .map(|(col_pos, def)| ColumnSchema::from_module_def(module_def, def, (), (table_id, col_pos.into())))
569 .collect();
570
571 let indexes = indexes
574 .values()
575 .map(|def| IndexSchema::from_module_def(module_def, def, table_id, IndexId::SENTINEL))
576 .collect();
577
578 let sequences = sequences
579 .values()
580 .map(|def| SequenceSchema::from_module_def(module_def, def, table_id, SequenceId::SENTINEL))
581 .collect();
582
583 let constraints = constraints
584 .values()
585 .map(|def| ConstraintSchema::from_module_def(module_def, def, table_id, ConstraintId::SENTINEL))
586 .collect();
587
588 let schedule = schedule
589 .as_ref()
590 .map(|schedule| ScheduleSchema::from_module_def(module_def, schedule, table_id, ScheduleId::SENTINEL));
591
592 TableSchema::new(
593 table_id,
594 (*name).clone().into(),
595 columns,
596 indexes,
597 constraints,
598 sequences,
599 (*table_type).into(),
600 (*table_access).into(),
601 schedule,
602 *primary_key,
603 )
604 }
605
606 fn check_compatible(&self, module_def: &ModuleDef, def: &Self::Def) -> Result<(), anyhow::Error> {
607 ensure_eq!(&self.table_name[..], &def.name[..], "Table name mismatch");
608 ensure_eq!(self.primary_key, def.primary_key, "Primary key mismatch");
609 let def_table_access: StAccess = (def.table_access).into();
610 ensure_eq!(self.table_access, def_table_access, "Table access mismatch");
611 let def_table_type: StTableType = (def.table_type).into();
612 ensure_eq!(self.table_type, def_table_type, "Table type mismatch");
613
614 for col in &self.columns {
615 let col_def = def
616 .columns
617 .get(col.col_pos.0 as usize)
618 .ok_or_else(|| anyhow::anyhow!("Column {} not found in definition", col.col_pos.0))?;
619 col.check_compatible(module_def, col_def)?;
620 }
621 ensure_eq!(self.columns.len(), def.columns.len(), "Column count mismatch");
622
623 for index in &self.indexes {
624 let index_def = def
625 .indexes
626 .get(&index.index_name[..])
627 .ok_or_else(|| anyhow::anyhow!("Index {} not found in definition", index.index_id.0))?;
628 index.check_compatible(module_def, index_def)?;
629 }
630 ensure_eq!(self.indexes.len(), def.indexes.len(), "Index count mismatch");
631
632 for constraint in &self.constraints {
633 let constraint_def = def
634 .constraints
635 .get(&constraint.constraint_name[..])
636 .ok_or_else(|| anyhow::anyhow!("Constraint {} not found in definition", constraint.constraint_id.0))?;
637 constraint.check_compatible(module_def, constraint_def)?;
638 }
639 ensure_eq!(
640 self.constraints.len(),
641 def.constraints.len(),
642 "Constraint count mismatch"
643 );
644
645 for sequence in &self.sequences {
646 let sequence_def = def
647 .sequences
648 .get(&sequence.sequence_name[..])
649 .ok_or_else(|| anyhow::anyhow!("Sequence {} not found in definition", sequence.sequence_id.0))?;
650 sequence.check_compatible(module_def, sequence_def)?;
651 }
652 ensure_eq!(self.sequences.len(), def.sequences.len(), "Sequence count mismatch");
653
654 if let Some(schedule) = &self.schedule {
655 let schedule_def = def
656 .schedule
657 .as_ref()
658 .ok_or_else(|| anyhow::anyhow!("Schedule not found in definition"))?;
659 schedule.check_compatible(module_def, schedule_def)?;
660 }
661 ensure_eq!(
662 self.schedule.is_some(),
663 def.schedule.is_some(),
664 "Schedule presence mismatch"
665 );
666 Ok(())
667 }
668}
669
670impl From<&TableSchema> for ProductType {
671 fn from(value: &TableSchema) -> Self {
672 ProductType::new(
673 value
674 .columns
675 .iter()
676 .map(|c| ProductTypeElement {
677 name: Some(c.col_name.clone()),
678 algebraic_type: c.col_type.clone(),
679 })
680 .collect(),
681 )
682 }
683}
684
685impl From<&TableSchema> for DbTable {
686 fn from(value: &TableSchema) -> Self {
687 DbTable::new(
688 Arc::new(value.into()),
689 value.table_id,
690 value.table_type,
691 value.table_access,
692 )
693 }
694}
695
696impl From<&TableSchema> for Header {
697 fn from(value: &TableSchema) -> Self {
698 let fields = value
699 .columns
700 .iter()
701 .map(|x| Column::new(FieldName::new(value.table_id, x.col_pos), x.col_type.clone()))
702 .collect();
703
704 Header::new(
705 value.table_id,
706 value.table_name.clone(),
707 fields,
708 value.backcompat_constraints(),
709 )
710 }
711}
712
713impl From<TableSchema> for Header {
714 fn from(schema: TableSchema) -> Self {
715 Header::from(&schema)
717 }
718}
719
720#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd)]
722pub struct ColumnSchema {
723 pub table_id: TableId,
725 pub col_pos: ColId,
727 pub col_name: Box<str>,
729 pub col_type: AlgebraicType,
732}
733
734impl Schema for ColumnSchema {
735 type Def = ColumnDef;
736 type ParentId = ();
737 type Id = (TableId, ColId);
740
741 fn from_module_def(
742 module_def: &ModuleDef,
743 def: &ColumnDef,
744 _parent_id: (),
745 (table_id, col_pos): (TableId, ColId),
746 ) -> Self {
747 let col_type = WithTypespace::new(module_def.typespace(), &def.ty)
748 .resolve_refs()
749 .expect("validated module should have all types resolve");
750 ColumnSchema {
751 table_id,
752 col_pos,
753 col_name: (*def.name).into(),
754 col_type,
755 }
756 }
757
758 fn check_compatible(&self, module_def: &ModuleDef, def: &Self::Def) -> Result<(), anyhow::Error> {
759 ensure_eq!(&self.col_name[..], &def.name[..], "Column name mismatch");
760 let resolved_def_ty = WithTypespace::new(module_def.typespace(), &def.ty).resolve_refs()?;
761 ensure_eq!(self.col_type, resolved_def_ty, "Column type mismatch");
762 ensure_eq!(self.col_pos, def.col_id, "Columnh ID mismatch");
763 Ok(())
764 }
765}
766
767impl From<&ColumnSchema> for ProductTypeElement {
768 fn from(value: &ColumnSchema) -> Self {
769 Self {
770 name: Some(value.col_name.clone()),
771 algebraic_type: value.col_type.clone(),
772 }
773 }
774}
775
776impl From<ColumnSchema> for Column {
777 fn from(schema: ColumnSchema) -> Self {
778 Column {
779 field: FieldName {
780 table: schema.table_id,
781 col: schema.col_pos,
782 },
783 algebraic_type: schema.col_type,
784 }
785 }
786}
787
788#[derive(Debug, Clone)]
790pub struct ColumnSchemaRef<'a> {
791 pub column: &'a ColumnSchema,
793 pub table_name: &'a str,
795}
796
797impl From<ColumnSchemaRef<'_>> for ProductTypeElement {
798 fn from(value: ColumnSchemaRef) -> Self {
799 ProductTypeElement::new(value.column.col_type.clone(), Some(value.column.col_name.clone()))
800 }
801}
802
803#[derive(Debug, Clone, PartialEq, Eq)]
805pub struct SequenceSchema {
806 pub sequence_id: SequenceId,
808 pub sequence_name: Box<str>,
811 pub table_id: TableId,
813 pub col_pos: ColId,
815 pub increment: i128,
817 pub start: i128,
819 pub min_value: i128,
821 pub max_value: i128,
823 pub allocated: i128,
825}
826
827impl Schema for SequenceSchema {
828 type Def = SequenceDef;
829 type Id = SequenceId;
830 type ParentId = TableId;
831
832 fn from_module_def(module_def: &ModuleDef, def: &Self::Def, parent_id: Self::ParentId, id: Self::Id) -> Self {
833 module_def.expect_contains(def);
834
835 SequenceSchema {
836 sequence_id: id,
837 sequence_name: (*def.name).into(),
838 table_id: parent_id,
839 col_pos: def.column,
840 increment: def.increment,
841 start: def.start.unwrap_or(1),
842 min_value: def.min_value.unwrap_or(1),
843 max_value: def.max_value.unwrap_or(i128::MAX),
844 allocated: 0, }
846 }
847
848 fn check_compatible(&self, _module_def: &ModuleDef, def: &Self::Def) -> Result<(), anyhow::Error> {
849 ensure_eq!(&self.sequence_name[..], &def.name[..], "Sequence name mismatch");
850 ensure_eq!(self.col_pos, def.column, "Sequence column mismatch");
851 ensure_eq!(self.increment, def.increment, "Sequence increment mismatch");
852 if let Some(start) = &def.start {
853 ensure_eq!(self.start, *start, "Sequence start mismatch");
854 }
855 if let Some(min_value) = &def.min_value {
856 ensure_eq!(self.min_value, *min_value, "Sequence min_value mismatch");
857 }
858 if let Some(max_value) = &def.max_value {
859 ensure_eq!(self.max_value, *max_value, "Sequence max_value mismatch");
860 }
861 Ok(())
862 }
863}
864
865#[derive(Debug, Clone, PartialEq, Eq)]
867pub struct ScheduleSchema {
868 pub table_id: TableId,
870
871 pub schedule_id: ScheduleId,
873
874 pub schedule_name: Box<str>,
876
877 pub reducer_name: Box<str>,
879
880 pub at_column: ColId,
882}
883
884impl Schema for ScheduleSchema {
885 type Def = ScheduleDef;
886
887 type Id = ScheduleId;
888
889 type ParentId = TableId;
890
891 fn from_module_def(module_def: &ModuleDef, def: &Self::Def, parent_id: Self::ParentId, id: Self::Id) -> Self {
892 module_def.expect_contains(def);
893
894 ScheduleSchema {
895 table_id: parent_id,
896 schedule_id: id,
897 schedule_name: (*def.name).into(),
898 reducer_name: (*def.reducer_name).into(),
899 at_column: def.at_column,
900 }
902 }
903
904 fn check_compatible(&self, _module_def: &ModuleDef, def: &Self::Def) -> Result<(), anyhow::Error> {
905 ensure_eq!(&self.schedule_name[..], &def.name[..], "Schedule name mismatch");
906 ensure_eq!(
907 &self.reducer_name[..],
908 &def.reducer_name[..],
909 "Schedule reducer name mismatch"
910 );
911 Ok(())
912 }
913}
914
915#[derive(Debug, Clone, PartialEq, Eq)]
917pub struct IndexSchema {
918 pub index_id: IndexId,
920 pub table_id: TableId,
922 pub index_name: Box<str>,
925 pub index_algorithm: IndexAlgorithm,
927}
928
929impl IndexSchema {}
930
931impl Schema for IndexSchema {
932 type Def = IndexDef;
933 type Id = IndexId;
934 type ParentId = TableId;
935
936 fn from_module_def(module_def: &ModuleDef, def: &Self::Def, parent_id: Self::ParentId, id: Self::Id) -> Self {
937 module_def.expect_contains(def);
938
939 let index_algorithm = def.algorithm.clone();
940 IndexSchema {
941 index_id: id,
942 table_id: parent_id,
943 index_name: (*def.name).into(),
944 index_algorithm,
945 }
946 }
947
948 fn check_compatible(&self, _module_def: &ModuleDef, def: &Self::Def) -> Result<(), anyhow::Error> {
949 ensure_eq!(&self.index_name[..], &def.name[..], "Index name mismatch");
950 ensure_eq!(&self.index_algorithm, &def.algorithm, "Index algorithm mismatch");
951 Ok(())
952 }
953}
954
955#[derive(Debug, Clone, PartialEq, Eq)]
960pub struct ConstraintSchema {
961 pub table_id: TableId,
963 pub constraint_id: ConstraintId,
965 pub constraint_name: Box<str>,
967 pub data: ConstraintData, }
970
971impl ConstraintSchema {
972 #[deprecated(note = "Use TableSchema::from_module_def instead")]
979 pub fn from_def(table_id: TableId, constraint: RawConstraintDefV8) -> Option<Self> {
980 if constraint.constraints.has_unique() {
981 Some(ConstraintSchema {
982 constraint_id: ConstraintId::SENTINEL, constraint_name: constraint.constraint_name.trim().into(),
984 table_id,
985 data: ConstraintData::Unique(UniqueConstraintData {
986 columns: constraint.columns.into(),
987 }),
988 })
989 } else {
990 None
991 }
992 }
993}
994
995impl Schema for ConstraintSchema {
996 type Def = ConstraintDef;
997 type Id = ConstraintId;
998 type ParentId = TableId;
999
1000 fn from_module_def(module_def: &ModuleDef, def: &Self::Def, parent_id: Self::ParentId, id: Self::Id) -> Self {
1001 module_def.expect_contains(def);
1002
1003 ConstraintSchema {
1004 constraint_id: id,
1005 constraint_name: (*def.name).into(),
1006 table_id: parent_id,
1007 data: def.data.clone(),
1008 }
1009 }
1010
1011 fn check_compatible(&self, _module_def: &ModuleDef, def: &Self::Def) -> Result<(), anyhow::Error> {
1012 ensure_eq!(&self.constraint_name[..], &def.name[..], "Constraint name mismatch");
1013 ensure_eq!(&self.data, &def.data, "Constraint data mismatch");
1014 Ok(())
1015 }
1016}
1017
1018#[derive(Debug, Clone, PartialEq, Eq)]
1020pub struct RowLevelSecuritySchema {
1021 pub table_id: TableId,
1022 pub sql: RawSql,
1023}