11use super :: traits:: { ColumnSchema , IndexSchema , SequenceId , SequenceSchema , TableId , TableSchema } ;
2+ use crate :: db:: datastore:: traits:: ConstraintSchema ;
23use crate :: error:: { DBError , TableError } ;
34use once_cell:: sync:: Lazy ;
45use spacetimedb_lib:: auth:: { StAccess , StTableType } ;
5- use spacetimedb_sats:: { product, AlgebraicType , AlgebraicValue , ProductType , ProductValue } ;
6+ use spacetimedb_lib:: ColumnIndexAttribute ;
7+ use spacetimedb_sats:: { product, AlgebraicType , AlgebraicValue , ArrayValue , ProductType , ProductValue } ;
68
79/// The static ID of the table that defines tables
810pub ( crate ) const ST_TABLES_ID : TableId = TableId ( 0 ) ;
@@ -12,20 +14,65 @@ pub(crate) const ST_COLUMNS_ID: TableId = TableId(1);
1214pub ( crate ) const ST_SEQUENCES_ID : TableId = TableId ( 2 ) ;
1315/// The static ID of the table that defines indexes
1416pub ( crate ) const ST_INDEXES_ID : TableId = TableId ( 3 ) ;
17+ /// The static ID of the table that defines constraints
18+ pub ( crate ) const ST_CONSTRAINTS_ID : TableId = TableId ( 4 ) ;
1519
1620pub ( crate ) const ST_TABLES_NAME : & str = "st_table" ;
1721pub ( crate ) const ST_COLUMNS_NAME : & str = "st_columns" ;
1822pub ( crate ) const ST_SEQUENCES_NAME : & str = "st_sequence" ;
1923pub ( crate ) const ST_INDEXES_NAME : & str = "st_indexes" ;
24+ pub ( crate ) const ST_CONSTRAINTS_NAME : & str = "st_constraints" ;
2025
2126pub ( crate ) const TABLE_ID_SEQUENCE_ID : SequenceId = SequenceId ( 0 ) ;
2227pub ( crate ) const SEQUENCE_ID_SEQUENCE_ID : SequenceId = SequenceId ( 1 ) ;
2328pub ( crate ) const INDEX_ID_SEQUENCE_ID : SequenceId = SequenceId ( 2 ) ;
29+ pub ( crate ) const CONSTRAINT_ID_SEQUENCE_ID : SequenceId = SequenceId ( 3 ) ;
2430
2531pub ( crate ) const ST_TABLE_ID_INDEX_ID : u32 = 0 ;
2632pub ( crate ) const ST_TABLE_NAME_INDEX_ID : u32 = 3 ;
2733pub ( crate ) const ST_INDEX_ID_INDEX_ID : u32 = 1 ;
2834pub ( crate ) const ST_SEQUENCE_ID_INDEX_ID : u32 = 2 ;
35+ pub ( crate ) const ST_CONSTRAINT_ID_INDEX_ID : u32 = 4 ;
36+ pub ( crate ) const ST_CONSTRAINT_ID_INDEX_HACK : u32 = 5 ;
37+ pub ( crate ) struct SystemTables { }
38+
39+ impl SystemTables {
40+ pub ( crate ) fn tables ( ) -> [ TableSchema ; 5 ] {
41+ [
42+ st_table_schema ( ) ,
43+ st_columns_schema ( ) ,
44+ st_sequences_schema ( ) ,
45+ st_indexes_schema ( ) ,
46+ st_constraints_schema ( ) ,
47+ ]
48+ }
49+
50+ pub ( crate ) fn total_tables ( ) -> usize {
51+ Self :: tables ( ) . len ( )
52+ }
53+
54+ pub ( crate ) fn total_indexes ( ) -> usize {
55+ Self :: tables ( ) . iter ( ) . flat_map ( |x| x. indexes . iter ( ) ) . count ( )
56+ }
57+
58+ pub ( crate ) fn total_constraints_indexes ( ) -> usize {
59+ Self :: tables ( )
60+ . iter ( )
61+ . flat_map ( |x| x. constraints . iter ( ) . filter ( |x| x. kind != ColumnIndexAttribute :: UNSET ) )
62+ . count ( )
63+ }
64+
65+ pub ( crate ) fn total_sequences ( ) -> usize {
66+ Self :: tables ( )
67+ . iter ( )
68+ . flat_map ( |x| x. columns . iter ( ) . filter ( |x| x. is_autoinc ) )
69+ . count ( )
70+ }
71+
72+ pub ( crate ) fn total_constraints ( ) -> usize {
73+ Self :: tables ( ) . iter ( ) . flat_map ( |x| x. constraints . iter ( ) ) . count ( )
74+ }
75+ }
2976
3077// WARNING: In order to keep a stable schema, don't change the discriminant of the fields
3178#[ derive( Debug ) ]
@@ -55,7 +102,7 @@ pub enum StColumnFields {
55102 ColId = 1 ,
56103 ColType = 2 ,
57104 ColName = 3 ,
58- ColIndexAttribute = 4 ,
105+ IsAutoInc = 4 ,
59106}
60107
61108impl StColumnFields {
@@ -66,7 +113,7 @@ impl StColumnFields {
66113 Self :: ColId => "col_id" ,
67114 Self :: ColType => "col_type" ,
68115 Self :: ColName => "col_name" ,
69- Self :: ColIndexAttribute => "col_idx_attr " ,
116+ Self :: IsAutoInc => "is_autoinc " ,
70117 }
71118 }
72119}
@@ -125,6 +172,29 @@ impl StSequenceFields {
125172 }
126173}
127174
175+ // WARNING: In order to keep a stable schema, don't change the discriminant of the fields
176+ #[ derive( Debug ) ]
177+ pub enum StConstraintFields {
178+ ConstraintId = 0 ,
179+ ConstraintName = 1 ,
180+ Kind = 2 ,
181+ TableId = 3 ,
182+ Columns = 4 ,
183+ }
184+
185+ impl StConstraintFields {
186+ pub fn name ( & self ) -> & ' static str {
187+ // WARNING: Don't change the name of the fields
188+ match self {
189+ Self :: ConstraintId => "constraint_id" ,
190+ Self :: ConstraintName => "constraint_name" ,
191+ Self :: Kind => "kind" ,
192+ Self :: TableId => "table_id" ,
193+ Self :: Columns => "columns" ,
194+ }
195+ }
196+ }
197+
128198/// System Table [ST_TABLES_NAME]
129199///
130200/// | table_id: u32 | table_name: String | table_type: String | table_access: String |
@@ -180,6 +250,7 @@ pub fn st_table_schema() -> TableSchema {
180250 is_autoinc: false ,
181251 } ,
182252 ] ,
253+ constraints : vec ! [ ] ,
183254 table_type : StTableType :: System ,
184255 table_access : StAccess :: Public ,
185256 }
@@ -202,40 +273,48 @@ pub fn st_columns_schema() -> TableSchema {
202273 // TODO(cloutiertyler): (table_id, col_id) should be have a unique constraint
203274 ColumnSchema {
204275 table_id: ST_COLUMNS_ID . 0 ,
205- col_id: 0 ,
206- col_name: "table_id" . into ( ) ,
276+ col_id: StColumnFields :: TableId as u32 ,
277+ col_name: StColumnFields :: TableId . name ( ) . to_string ( ) ,
207278 col_type: AlgebraicType :: U32 ,
208279 is_autoinc: false ,
209280 } ,
210281 ColumnSchema {
211282 table_id: ST_COLUMNS_ID . 0 ,
212- col_id: 1 ,
213- col_name: "col_id" . into ( ) ,
283+ col_id: StColumnFields :: ColId as u32 ,
284+ col_name: StColumnFields :: ColId . name ( ) . to_string ( ) ,
214285 col_type: AlgebraicType :: U32 ,
215286 is_autoinc: false ,
216287 } ,
217288 ColumnSchema {
218289 table_id: ST_COLUMNS_ID . 0 ,
219- col_id: 2 ,
220- col_name: "col_type" . into ( ) ,
290+ col_id: StColumnFields :: ColType as u32 ,
291+ col_name: StColumnFields :: ColType . name ( ) . to_string ( ) ,
221292 col_type: AlgebraicType :: bytes( ) ,
222293 is_autoinc: false ,
223294 } ,
224295 ColumnSchema {
225296 table_id: ST_COLUMNS_ID . 0 ,
226- col_id: 3 ,
227- col_name: "col_name" . into ( ) ,
297+ col_id: StColumnFields :: ColName as u32 ,
298+ col_name: StColumnFields :: ColName . name ( ) . to_string ( ) ,
228299 col_type: AlgebraicType :: String ,
229300 is_autoinc: false ,
230301 } ,
231302 ColumnSchema {
232303 table_id: ST_COLUMNS_ID . 0 ,
233- col_id: 4 ,
234- col_name: "is_autoinc" . into ( ) ,
304+ col_id: StColumnFields :: IsAutoInc as u32 ,
305+ col_name: StColumnFields :: IsAutoInc . name ( ) . to_string ( ) ,
235306 col_type: AlgebraicType :: Bool ,
236307 is_autoinc: false ,
237308 } ,
238309 ] ,
310+ constraints : vec ! [ ConstraintSchema {
311+ constraint_id: ST_CONSTRAINT_ID_INDEX_HACK ,
312+ constraint_name: "ct_columns_table_id" . to_string( ) ,
313+ kind: ColumnIndexAttribute :: INDEXED ,
314+ table_id: ST_COLUMNS_ID . 0 ,
315+ //TODO: Change to multi-columns when PR for it land: StColumnFields::ColId as u32
316+ columns: vec![ StColumnFields :: TableId as u32 ] ,
317+ } ] ,
239318 table_type : StTableType :: System ,
240319 table_access : StAccess :: Public ,
241320 }
@@ -298,6 +377,7 @@ pub fn st_indexes_schema() -> TableSchema {
298377 is_autoinc: false ,
299378 } ,
300379 ] ,
380+ constraints : vec ! [ ] ,
301381 table_type : StTableType :: System ,
302382 table_access : StAccess :: Public ,
303383 }
@@ -388,6 +468,7 @@ pub(crate) fn st_sequences_schema() -> TableSchema {
388468 is_autoinc: false ,
389469 } ,
390470 ] ,
471+ constraints : vec ! [ ] ,
391472 table_type : StTableType :: System ,
392473 table_access : StAccess :: Public ,
393474 }
@@ -396,6 +477,69 @@ pub(crate) fn st_sequences_schema() -> TableSchema {
396477pub static ST_SEQUENCE_ROW_TYPE : Lazy < ProductType > =
397478 Lazy :: new ( || ProductType :: from_iter ( st_sequences_schema ( ) . columns . iter ( ) . map ( |c| c. col_type . clone ( ) ) ) ) ;
398479
480+ /// System Table [ST_CONSTRAINTS_NAME]
481+ ///
482+ /// | constraint_id | constraint_name | kind | table_id | columns |
483+ /// |---------------|-------------------- -|-----------|-------|-----------|
484+ /// | 1 | "unique_customer_id" | 1 | 100 | [1, 4] |
485+ pub ( crate ) fn st_constraints_schema ( ) -> TableSchema {
486+ TableSchema {
487+ table_id : ST_CONSTRAINTS_ID . 0 ,
488+ table_name : ST_CONSTRAINTS_NAME . into ( ) ,
489+ // TODO: Unique constraint on sequence name?
490+ indexes : vec ! [ IndexSchema {
491+ index_id: ST_CONSTRAINT_ID_INDEX_ID ,
492+ table_id: ST_CONSTRAINTS_ID . 0 ,
493+ col_id: 0 ,
494+ index_name: "constraint_id_idx" . into( ) ,
495+ is_unique: true ,
496+ } ] ,
497+ columns : vec ! [
498+ ColumnSchema {
499+ table_id: ST_CONSTRAINTS_ID . 0 ,
500+ col_id: StConstraintFields :: ConstraintId as u32 ,
501+ col_name: StConstraintFields :: ConstraintId . name( ) . into( ) ,
502+ col_type: AlgebraicType :: U32 ,
503+ is_autoinc: true ,
504+ } ,
505+ ColumnSchema {
506+ table_id: ST_CONSTRAINTS_ID . 0 ,
507+ col_id: StConstraintFields :: ConstraintName as u32 ,
508+ col_name: StConstraintFields :: ConstraintName . name( ) . into( ) ,
509+ col_type: AlgebraicType :: String ,
510+ is_autoinc: false ,
511+ } ,
512+ ColumnSchema {
513+ table_id: ST_CONSTRAINTS_ID . 0 ,
514+ col_id: StConstraintFields :: Kind as u32 ,
515+ col_name: StConstraintFields :: Kind . name( ) . into( ) ,
516+ col_type: AlgebraicType :: U32 ,
517+ is_autoinc: false ,
518+ } ,
519+ ColumnSchema {
520+ table_id: ST_CONSTRAINTS_ID . 0 ,
521+ col_id: StConstraintFields :: TableId as u32 ,
522+ col_name: StConstraintFields :: TableId . name( ) . into( ) ,
523+ col_type: AlgebraicType :: U32 ,
524+ is_autoinc: false ,
525+ } ,
526+ ColumnSchema {
527+ table_id: ST_CONSTRAINTS_ID . 0 ,
528+ col_id: StConstraintFields :: Columns as u32 ,
529+ col_name: StConstraintFields :: Columns . name( ) . into( ) ,
530+ col_type: AlgebraicType :: array( AlgebraicType :: U32 ) ,
531+ is_autoinc: false ,
532+ } ,
533+ ] ,
534+ constraints : vec ! [ ] ,
535+ table_type : StTableType :: System ,
536+ table_access : StAccess :: Public ,
537+ }
538+ }
539+
540+ pub static ST_CONSTRAINT_ROW_TYPE : Lazy < ProductType > =
541+ Lazy :: new ( || ProductType :: from_iter ( st_constraints_schema ( ) . columns . iter ( ) . map ( |c| c. col_type . clone ( ) ) ) ) ;
542+
399543pub ( crate ) fn table_name_is_system ( table_name : & str ) -> bool {
400544 table_name. starts_with ( "st_" )
401545}
@@ -497,7 +641,7 @@ impl<'a> TryFrom<&'a ProductValue> for StColumnRow<&'a str> {
497641 AlgebraicType :: decode ( & mut & bytes[ ..] ) . map_err ( |e| TableError :: InvalidSchema ( table_id, e. into ( ) ) ) ?;
498642
499643 let col_name = row. field_as_str ( StColumnFields :: ColName as usize , None ) ?;
500- let is_autoinc = row. field_as_bool ( StColumnFields :: ColIndexAttribute as usize , None ) ?;
644+ let is_autoinc = row. field_as_bool ( StColumnFields :: IsAutoInc as usize , None ) ?;
501645
502646 Ok ( StColumnRow {
503647 table_id,
@@ -660,3 +804,61 @@ impl<'a> From<&StSequenceRow<&'a str>> for SequenceSchema {
660804 }
661805 }
662806}
807+
808+ #[ derive( Debug , PartialEq , Eq ) ]
809+ pub struct StConstraintRow < Name : AsRef < str > > {
810+ pub ( crate ) constraint_id : u32 ,
811+ pub ( crate ) constraint_name : Name ,
812+ pub ( crate ) kind : ColumnIndexAttribute ,
813+ pub ( crate ) table_id : u32 ,
814+ pub ( crate ) columns : Vec < u32 > ,
815+ }
816+
817+ impl StConstraintRow < & str > {
818+ pub fn to_owned ( & self ) -> StConstraintRow < String > {
819+ StConstraintRow {
820+ constraint_id : self . constraint_id ,
821+ constraint_name : self . constraint_name . to_string ( ) ,
822+ kind : self . kind ,
823+ table_id : self . table_id ,
824+ columns : self . columns . clone ( ) ,
825+ }
826+ }
827+ }
828+
829+ impl < ' a > TryFrom < & ' a ProductValue > for StConstraintRow < & ' a str > {
830+ type Error = DBError ;
831+ fn try_from ( row : & ' a ProductValue ) -> Result < StConstraintRow < & ' a str > , DBError > {
832+ let constraint_id = row. field_as_u32 ( StConstraintFields :: ConstraintId as usize , None ) ?;
833+ let constraint_name = row. field_as_str ( StConstraintFields :: ConstraintName as usize , None ) ?;
834+ let kind = row. field_as_u8 ( StConstraintFields :: Kind as usize , None ) ?;
835+ let kind = ColumnIndexAttribute :: try_from ( kind) . expect ( "Fail to decode ColumnIndexAttribute" ) ;
836+ let table_id = row. field_as_u32 ( StConstraintFields :: TableId as usize , None ) ?;
837+ let columns = row. field_as_array ( StConstraintFields :: Columns as usize , None ) ?;
838+ let columns = if let ArrayValue :: U32 ( x) = columns {
839+ x. clone ( )
840+ } else {
841+ panic ! ( )
842+ } ;
843+
844+ Ok ( StConstraintRow {
845+ constraint_id,
846+ constraint_name,
847+ kind,
848+ table_id,
849+ columns,
850+ } )
851+ }
852+ }
853+
854+ impl < Name : AsRef < str > > From < & StConstraintRow < Name > > for ProductValue {
855+ fn from ( x : & StConstraintRow < Name > ) -> Self {
856+ product ! [
857+ AlgebraicValue :: U32 ( x. constraint_id) ,
858+ AlgebraicValue :: String ( x. constraint_name. as_ref( ) . to_string( ) ) ,
859+ AlgebraicValue :: U8 ( x. kind. bits( ) ) ,
860+ AlgebraicValue :: U32 ( x. table_id) ,
861+ AlgebraicValue :: ArrayOf ( x. columns. clone( ) )
862+ ]
863+ }
864+ }
0 commit comments