Skip to content

Commit 5389b8d

Browse files
authored
Create st_constraints system table (#225)
* Create st_constraints * Add constranints to bootstraping * Clippy
1 parent bb322b5 commit 5389b8d

File tree

6 files changed

+393
-64
lines changed

6 files changed

+393
-64
lines changed

crates/core/src/db/datastore/locking_tx_datastore/mod.rs

Lines changed: 138 additions & 40 deletions
Large diffs are not rendered by default.

crates/core/src/db/datastore/system_tables.rs

Lines changed: 216 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
use super::traits::{ColumnSchema, IndexSchema, SequenceId, SequenceSchema, TableId, TableSchema};
2+
use crate::db::datastore::traits::ConstraintSchema;
23
use crate::error::{DBError, TableError};
34
use once_cell::sync::Lazy;
45
use 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
810
pub(crate) const ST_TABLES_ID: TableId = TableId(0);
@@ -12,20 +14,65 @@ pub(crate) const ST_COLUMNS_ID: TableId = TableId(1);
1214
pub(crate) const ST_SEQUENCES_ID: TableId = TableId(2);
1315
/// The static ID of the table that defines indexes
1416
pub(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

1620
pub(crate) const ST_TABLES_NAME: &str = "st_table";
1721
pub(crate) const ST_COLUMNS_NAME: &str = "st_columns";
1822
pub(crate) const ST_SEQUENCES_NAME: &str = "st_sequence";
1923
pub(crate) const ST_INDEXES_NAME: &str = "st_indexes";
24+
pub(crate) const ST_CONSTRAINTS_NAME: &str = "st_constraints";
2025

2126
pub(crate) const TABLE_ID_SEQUENCE_ID: SequenceId = SequenceId(0);
2227
pub(crate) const SEQUENCE_ID_SEQUENCE_ID: SequenceId = SequenceId(1);
2328
pub(crate) const INDEX_ID_SEQUENCE_ID: SequenceId = SequenceId(2);
29+
pub(crate) const CONSTRAINT_ID_SEQUENCE_ID: SequenceId = SequenceId(3);
2430

2531
pub(crate) const ST_TABLE_ID_INDEX_ID: u32 = 0;
2632
pub(crate) const ST_TABLE_NAME_INDEX_ID: u32 = 3;
2733
pub(crate) const ST_INDEX_ID_INDEX_ID: u32 = 1;
2834
pub(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

61108
impl 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 {
396477
pub 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+
399543
pub(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+
}

crates/core/src/db/datastore/traits.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::db::relational_db::ST_TABLES_ID;
22
use core::fmt;
33
use spacetimedb_lib::auth::{StAccess, StTableType};
44
use spacetimedb_lib::relation::{DbTable, FieldName, FieldOnly, Header, TableField};
5-
use spacetimedb_lib::DataKey;
5+
use spacetimedb_lib::{ColumnIndexAttribute, DataKey};
66
use spacetimedb_sats::{AlgebraicType, AlgebraicValue, ProductType, ProductTypeElement, ProductValue};
77
use spacetimedb_vm::expr::SourceExpr;
88
use std::{ops::RangeBounds, sync::Arc};
@@ -158,12 +158,31 @@ impl From<ColumnSchema> for ColumnDef {
158158
}
159159
}
160160

161+
#[derive(Debug, Clone, PartialEq, Eq)]
162+
pub struct ConstraintSchema {
163+
pub(crate) constraint_id: u32,
164+
pub(crate) constraint_name: String,
165+
pub(crate) kind: ColumnIndexAttribute,
166+
pub(crate) table_id: u32,
167+
pub(crate) columns: Vec<u32>,
168+
}
169+
170+
/// This type is just the [ConstraintSchema] without the autoinc fields
171+
#[derive(Debug, Clone, PartialEq, Eq)]
172+
pub struct ConstraintDef {
173+
pub(crate) constraint_name: String,
174+
pub(crate) kind: ColumnIndexAttribute,
175+
pub(crate) table_id: u32,
176+
pub(crate) columns: Vec<u32>,
177+
}
178+
161179
#[derive(Debug, Clone, PartialEq, Eq)]
162180
pub struct TableSchema {
163181
pub(crate) table_id: u32,
164182
pub(crate) table_name: String,
165183
pub(crate) columns: Vec<ColumnSchema>,
166184
pub(crate) indexes: Vec<IndexSchema>,
185+
pub(crate) constraints: Vec<ConstraintSchema>,
167186
pub(crate) table_type: StTableType,
168187
pub(crate) table_access: StAccess,
169188
}

crates/core/src/vm.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -614,7 +614,7 @@ pub(crate) mod tests {
614614
table_id: 2,
615615
col_id: 0,
616616
increment: 1,
617-
start: 3,
617+
start: 4,
618618
min_value: 1,
619619
max_value: 4294967295,
620620
allocated: 4096,

0 commit comments

Comments
 (0)