1use spacetimedb_lib::db::auth::{StAccess, StTableType};
15use spacetimedb_lib::db::raw_def::v9::{btree, RawSql};
16use spacetimedb_lib::db::raw_def::*;
17use spacetimedb_lib::de::{Deserialize, DeserializeOwned, Error};
18use spacetimedb_lib::ser::Serialize;
19use spacetimedb_lib::st_var::StVarValue;
20use spacetimedb_lib::{ConnectionId, Identity, ProductValue, SpacetimeType};
21use spacetimedb_primitives::*;
22use spacetimedb_sats::algebraic_value::ser::value_serialize;
23use spacetimedb_sats::hash::Hash;
24use spacetimedb_sats::product_value::InvalidFieldError;
25use spacetimedb_sats::{impl_deserialize, impl_serialize, impl_st, u256, AlgebraicType, AlgebraicValue, ArrayValue};
26use spacetimedb_schema::def::{
27 BTreeAlgorithm, ConstraintData, DirectAlgorithm, IndexAlgorithm, ModuleDef, UniqueConstraintData,
28};
29use spacetimedb_schema::schema::{
30 ColumnSchema, ConstraintSchema, IndexSchema, RowLevelSecuritySchema, ScheduleSchema, Schema, SequenceSchema,
31 TableSchema,
32};
33use spacetimedb_table::table::RowRef;
34use std::cell::RefCell;
35use std::str::FromStr;
36use strum::Display;
37use v9::{RawModuleDefV9Builder, TableType};
38
39use super::error::DatastoreError;
40
41pub(crate) const ST_TABLE_ID: TableId = TableId(1);
43pub(crate) const ST_COLUMN_ID: TableId = TableId(2);
45pub(crate) const ST_SEQUENCE_ID: TableId = TableId(3);
47pub(crate) const ST_INDEX_ID: TableId = TableId(4);
49pub(crate) const ST_CONSTRAINT_ID: TableId = TableId(5);
51pub(crate) const ST_MODULE_ID: TableId = TableId(6);
54pub(crate) const ST_CLIENT_ID: TableId = TableId(7);
56pub(crate) const ST_VAR_ID: TableId = TableId(8);
58pub(crate) const ST_SCHEDULED_ID: TableId = TableId(9);
60
61pub(crate) const ST_ROW_LEVEL_SECURITY_ID: TableId = TableId(10);
63pub(crate) const ST_TABLE_NAME: &str = "st_table";
64pub(crate) const ST_COLUMN_NAME: &str = "st_column";
65pub(crate) const ST_SEQUENCE_NAME: &str = "st_sequence";
66pub(crate) const ST_INDEX_NAME: &str = "st_index";
67pub(crate) const ST_CONSTRAINT_NAME: &str = "st_constraint";
68pub(crate) const ST_MODULE_NAME: &str = "st_module";
69pub(crate) const ST_CLIENT_NAME: &str = "st_client";
70pub(crate) const ST_SCHEDULED_NAME: &str = "st_scheduled";
71pub(crate) const ST_VAR_NAME: &str = "st_var";
72pub(crate) const ST_ROW_LEVEL_SECURITY_NAME: &str = "st_row_level_security";
73pub(crate) const ST_RESERVED_SEQUENCE_RANGE: u32 = 4096;
87
88#[allow(non_camel_case_types)]
90#[derive(Debug, Display)]
91pub enum SystemTable {
92 st_table,
93 st_column,
94 st_sequence,
95 st_index,
96 st_constraint,
97 st_row_level_security,
98}
99
100pub(crate) fn system_tables() -> [TableSchema; 10] {
101 [
102 st_table_schema(),
104 st_column_schema(),
105 st_index_schema(),
106 st_constraint_schema(),
107 st_module_schema(),
108 st_client_schema(),
109 st_var_schema(),
110 st_scheduled_schema(),
111 st_row_level_security_schema(),
112 st_sequence_schema(),
115 ]
116}
117
118pub trait StFields: Copy + Sized {
120 fn col_id(self) -> ColId;
122
123 #[inline]
125 fn col_idx(self) -> usize {
126 self.col_id().idx()
127 }
128
129 fn name(self) -> &'static str;
131
132 #[inline]
134 fn col_name(self) -> Box<str> {
135 self.name().into()
136 }
137
138 fn fields() -> &'static [Self];
140}
141
142pub(crate) const ST_TABLE_IDX: usize = 0;
144pub(crate) const ST_COLUMN_IDX: usize = 1;
145pub(crate) const ST_INDEX_IDX: usize = 2;
146pub(crate) const ST_CONSTRAINT_IDX: usize = 3;
147pub(crate) const ST_MODULE_IDX: usize = 4;
148pub(crate) const ST_CLIENT_IDX: usize = 5;
149pub(crate) const ST_VAR_IDX: usize = 6;
150pub(crate) const ST_SCHEDULED_IDX: usize = 7;
151pub(crate) const ST_ROW_LEVEL_SECURITY_IDX: usize = 8;
152pub(crate) const ST_SEQUENCE_IDX: usize = 9;
154
155macro_rules! st_fields_enum {
156 ($(#[$attr:meta])* enum $ty_name:ident { $($name:expr, $var:ident = $discr:expr,)* }) => {
157 #[derive(Copy, Clone, Debug)]
158 $(#[$attr])*
159 pub enum $ty_name {
160 $($var = $discr,)*
161 }
162
163 impl StFields for $ty_name {
164 #[inline]
165 fn col_id(self) -> ColId {
166 ColId(self as _)
167 }
168
169 #[inline]
170 fn name(self) -> &'static str {
171 match self {
172 $(Self::$var => $name,)*
173 }
174 }
175
176 fn fields() -> &'static [$ty_name] {
177 &[$($ty_name::$var,)*]
178 }
179 }
180
181 impl From<$ty_name> for ColId {
182 fn from(value: $ty_name) -> Self {
183 value.col_id()
184 }
185 }
186 }
187}
188
189st_fields_enum!(enum StTableFields {
191 "table_id", TableId = 0,
192 "table_name", TableName = 1,
193 "table_type", TableType = 2,
194 "table_access", TablesAccess = 3,
195 "table_primary_key", PrimaryKey = 4,
196});
197st_fields_enum!(enum StColumnFields {
199 "table_id", TableId = 0,
200 "col_pos", ColPos = 1,
201 "col_name", ColName = 2,
202 "col_type", ColType = 3,
203});
204st_fields_enum!(enum StIndexFields {
206 "index_id", IndexId = 0,
207 "table_id", TableId = 1,
208 "index_name", IndexName = 2,
209 "index_algorithm", IndexAlgorithm = 3,
210});
211st_fields_enum!(
213 enum StSequenceFields {
215 "sequence_id", SequenceId = 0,
216 "sequence_name", SequenceName = 1,
217 "table_id", TableId = 2,
218 "col_pos", ColPos = 3,
219 "increment", Increment = 4,
220 "start", Start = 5,
221 "min_value", MinValue = 6,
222 "max_value", MaxValue = 7,
223 "allocated", Allocated = 8,
224});
225st_fields_enum!(enum StConstraintFields {
227 "constraint_id", ConstraintId = 0,
228 "constraint_name", ConstraintName = 1,
229 "table_id", TableId = 2,
230 "constraint_data", ConstraintData = 3,
231});
232st_fields_enum!(enum StRowLevelSecurityFields {
234 "table_id", TableId = 0,
235 "sql", Sql = 1,
236});
237st_fields_enum!(enum StModuleFields {
239 "database_identity", DatabaseIdentity = 0,
240 "owner_identity", OwnerIdentity = 1,
241 "program_kind", ProgramKind = 2,
242 "program_hash", ProgramHash = 3,
243 "program_bytes", ProgramBytes = 4,
244 "module_version", ModuleVersion = 5,
245});
246st_fields_enum!(enum StClientFields {
248 "identity", Identity = 0,
249 "connection_id", ConnectionId = 1,
250});
251st_fields_enum!(enum StVarFields {
253 "name", Name = 0,
254 "value", Value = 1,
255});
256
257st_fields_enum!(enum StScheduledFields {
258 "schedule_id", ScheduleId = 0,
259 "table_id", TableId = 1,
260 "reducer_name", ReducerName = 2,
261 "schedule_name", ScheduleName = 3,
262 "at_column", AtColumn = 4,
263});
264
265fn validate_system_table<T: StFields + 'static>(def: &ModuleDef, table_name: &str) {
269 let table = def.table(table_name).expect("missing system table definition");
270 let fields = T::fields();
271 assert_eq!(table.columns.len(), fields.len());
272 for field in T::fields() {
273 let col = table
274 .columns
275 .get(field.col_id().idx())
276 .expect("missing system table field");
277 assert_eq!(&col.name[..], field.name());
278 }
279}
280
281fn system_module_def() -> ModuleDef {
283 let mut builder = RawModuleDefV9Builder::new();
284
285 let st_table_type = builder.add_type::<StTableRow>();
286 builder
287 .build_table(ST_TABLE_NAME, *st_table_type.as_ref().expect("should be ref"))
288 .with_type(TableType::System)
289 .with_auto_inc_primary_key(StTableFields::TableId)
290 .with_index_no_accessor_name(btree(StTableFields::TableId))
291 .with_unique_constraint(StTableFields::TableName)
292 .with_index_no_accessor_name(btree(StTableFields::TableName));
293
294 let st_raw_column_type = builder.add_type::<StColumnRow>();
295 let st_col_row_unique_cols = [StColumnFields::TableId.col_id(), StColumnFields::ColPos.col_id()];
296 builder
297 .build_table(ST_COLUMN_NAME, *st_raw_column_type.as_ref().expect("should be ref"))
298 .with_type(TableType::System)
299 .with_unique_constraint(st_col_row_unique_cols)
300 .with_index_no_accessor_name(btree(st_col_row_unique_cols));
301
302 let st_index_type = builder.add_type::<StIndexRow>();
303 builder
304 .build_table(ST_INDEX_NAME, *st_index_type.as_ref().expect("should be ref"))
305 .with_type(TableType::System)
306 .with_auto_inc_primary_key(StIndexFields::IndexId)
307 .with_index_no_accessor_name(btree(StIndexFields::IndexId));
308 let st_sequence_type = builder.add_type::<StSequenceRow>();
311 builder
312 .build_table(ST_SEQUENCE_NAME, *st_sequence_type.as_ref().expect("should be ref"))
313 .with_type(TableType::System)
314 .with_auto_inc_primary_key(StSequenceFields::SequenceId)
315 .with_index_no_accessor_name(btree(StSequenceFields::SequenceId));
316 let st_constraint_type = builder.add_type::<StConstraintRow>();
319 builder
320 .build_table(ST_CONSTRAINT_NAME, *st_constraint_type.as_ref().expect("should be ref"))
321 .with_type(TableType::System)
322 .with_auto_inc_primary_key(StConstraintFields::ConstraintId)
323 .with_index_no_accessor_name(btree(StConstraintFields::ConstraintId));
324 let st_row_level_security_type = builder.add_type::<StRowLevelSecurityRow>();
327 builder
328 .build_table(
329 ST_ROW_LEVEL_SECURITY_NAME,
330 *st_row_level_security_type.as_ref().expect("should be ref"),
331 )
332 .with_type(TableType::System)
333 .with_primary_key(StRowLevelSecurityFields::Sql)
334 .with_unique_constraint(StRowLevelSecurityFields::Sql)
335 .with_index_no_accessor_name(btree(StRowLevelSecurityFields::Sql))
336 .with_index_no_accessor_name(btree(StRowLevelSecurityFields::TableId));
337
338 let st_module_type = builder.add_type::<StModuleRow>();
339 builder
340 .build_table(ST_MODULE_NAME, *st_module_type.as_ref().expect("should be ref"))
341 .with_type(TableType::System);
342 let st_client_type = builder.add_type::<StClientRow>();
345 let st_client_unique_cols = [StClientFields::Identity, StClientFields::ConnectionId];
346 builder
347 .build_table(ST_CLIENT_NAME, *st_client_type.as_ref().expect("should be ref"))
348 .with_type(TableType::System)
349 .with_unique_constraint(st_client_unique_cols) .with_index_no_accessor_name(btree(st_client_unique_cols));
351
352 let st_schedule_type = builder.add_type::<StScheduledRow>();
353 builder
354 .build_table(ST_SCHEDULED_NAME, *st_schedule_type.as_ref().expect("should be ref"))
355 .with_type(TableType::System)
356 .with_unique_constraint(StScheduledFields::TableId) .with_index_no_accessor_name(btree(StScheduledFields::TableId))
358 .with_auto_inc_primary_key(StScheduledFields::ScheduleId) .with_index_no_accessor_name(btree(StScheduledFields::ScheduleId));
360 let st_var_type = builder.add_type::<StVarRow>();
363 builder
364 .build_table(ST_VAR_NAME, *st_var_type.as_ref().expect("should be ref"))
365 .with_type(TableType::System)
366 .with_unique_constraint(StVarFields::Name) .with_index_no_accessor_name(btree(StVarFields::Name))
368 .with_primary_key(StVarFields::Name);
369
370 let result = builder
371 .finish()
372 .try_into()
373 .expect("system table module is invalid, did you change it or add a validation rule it doesn't meet?");
374
375 validate_system_table::<StTableFields>(&result, ST_TABLE_NAME);
376 validate_system_table::<StColumnFields>(&result, ST_COLUMN_NAME);
377 validate_system_table::<StIndexFields>(&result, ST_INDEX_NAME);
378 validate_system_table::<StSequenceFields>(&result, ST_SEQUENCE_NAME);
379 validate_system_table::<StConstraintFields>(&result, ST_CONSTRAINT_NAME);
380 validate_system_table::<StRowLevelSecurityFields>(&result, ST_ROW_LEVEL_SECURITY_NAME);
381 validate_system_table::<StModuleFields>(&result, ST_MODULE_NAME);
382 validate_system_table::<StClientFields>(&result, ST_CLIENT_NAME);
383 validate_system_table::<StVarFields>(&result, ST_VAR_NAME);
384 validate_system_table::<StScheduledFields>(&result, ST_SCHEDULED_NAME);
385
386 result
387}
388
389lazy_static::lazy_static! {
390 static ref SYSTEM_MODULE_DEF: ModuleDef = system_module_def();
401}
402
403fn st_schema(name: &str, id: TableId) -> TableSchema {
404 let result = TableSchema::from_module_def(
405 &SYSTEM_MODULE_DEF,
406 SYSTEM_MODULE_DEF.table(name).expect("missing system table definition"),
407 (),
408 id,
409 );
410 result
411}
412
413fn st_table_schema() -> TableSchema {
414 st_schema(ST_TABLE_NAME, ST_TABLE_ID)
415}
416
417fn st_column_schema() -> TableSchema {
418 st_schema(ST_COLUMN_NAME, ST_COLUMN_ID)
419}
420
421fn st_index_schema() -> TableSchema {
422 st_schema(ST_INDEX_NAME, ST_INDEX_ID)
423}
424
425fn st_sequence_schema() -> TableSchema {
426 st_schema(ST_SEQUENCE_NAME, ST_SEQUENCE_ID)
427}
428
429fn st_constraint_schema() -> TableSchema {
430 st_schema(ST_CONSTRAINT_NAME, ST_CONSTRAINT_ID)
431}
432
433fn st_row_level_security_schema() -> TableSchema {
434 st_schema(ST_ROW_LEVEL_SECURITY_NAME, ST_ROW_LEVEL_SECURITY_ID)
435}
436
437pub(crate) fn st_module_schema() -> TableSchema {
438 st_schema(ST_MODULE_NAME, ST_MODULE_ID)
439}
440
441fn st_client_schema() -> TableSchema {
442 st_schema(ST_CLIENT_NAME, ST_CLIENT_ID)
443}
444
445fn st_scheduled_schema() -> TableSchema {
446 st_schema(ST_SCHEDULED_NAME, ST_SCHEDULED_ID)
447}
448
449pub fn st_var_schema() -> TableSchema {
450 st_schema(ST_VAR_NAME, ST_VAR_ID)
451}
452
453pub(crate) fn system_table_schema(table_id: TableId) -> Option<TableSchema> {
460 match table_id {
461 ST_TABLE_ID => Some(st_table_schema()),
462 ST_COLUMN_ID => Some(st_column_schema()),
463 ST_SEQUENCE_ID => Some(st_sequence_schema()),
464 ST_INDEX_ID => Some(st_index_schema()),
465 ST_CONSTRAINT_ID => Some(st_constraint_schema()),
466 ST_ROW_LEVEL_SECURITY_ID => Some(st_row_level_security_schema()),
467 ST_MODULE_ID => Some(st_module_schema()),
468 ST_CLIENT_ID => Some(st_client_schema()),
469 ST_VAR_ID => Some(st_var_schema()),
470 ST_SCHEDULED_ID => Some(st_scheduled_schema()),
471 _ => None,
472 }
473}
474
475#[derive(Debug, Clone, PartialEq, Eq, SpacetimeType)]
481#[sats(crate = spacetimedb_lib)]
482pub struct StTableRow {
483 pub(crate) table_id: TableId,
484 pub(crate) table_name: Box<str>,
485 pub(crate) table_type: StTableType,
486 pub(crate) table_access: StAccess,
487 pub(crate) table_primary_key: Option<ColList>,
491}
492
493impl TryFrom<RowRef<'_>> for StTableRow {
494 type Error = DatastoreError;
495 fn try_from(row: RowRef<'_>) -> Result<Self, DatastoreError> {
496 read_via_bsatn(row)
497 }
498}
499
500impl From<StTableRow> for ProductValue {
501 fn from(x: StTableRow) -> Self {
502 to_product_value(&x)
503 }
504}
505
506#[derive(Debug, Clone, PartialEq, Eq)]
508pub(crate) struct AlgebraicTypeViaBytes(pub AlgebraicType);
509impl_st!([] AlgebraicTypeViaBytes, AlgebraicType::bytes());
510impl<'de> Deserialize<'de> for AlgebraicTypeViaBytes {
511 fn deserialize<D: spacetimedb_lib::de::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
512 let bytes = <&[u8]>::deserialize(deserializer)?;
513 let ty = AlgebraicType::decode(&mut &*bytes).map_err(D::Error::custom)?;
514 Ok(AlgebraicTypeViaBytes(ty))
515 }
516}
517thread_local! {
518 static ALGEBRAIC_TYPE_WRITE_BUF: RefCell<Vec<u8>> = const { RefCell::new(Vec::new()) };
519}
520impl_serialize!([] AlgebraicTypeViaBytes, (self, ser) => {
521 ALGEBRAIC_TYPE_WRITE_BUF.with_borrow_mut(|buf| {
522 buf.clear();
523 self.0.encode(buf);
524 buf[..].serialize(ser)
525 })
526});
527impl From<AlgebraicType> for AlgebraicTypeViaBytes {
528 fn from(ty: AlgebraicType) -> Self {
529 Self(ty)
530 }
531}
532
533#[derive(Debug, Clone, PartialEq, Eq, SpacetimeType)]
539#[sats(crate = spacetimedb_lib)]
540pub struct StColumnRow {
541 pub(crate) table_id: TableId,
542 pub(crate) col_pos: ColId,
543 pub(crate) col_name: Box<str>,
544 pub(crate) col_type: AlgebraicTypeViaBytes,
545}
546
547impl TryFrom<RowRef<'_>> for StColumnRow {
548 type Error = DatastoreError;
549 fn try_from(row: RowRef<'_>) -> Result<Self, DatastoreError> {
550 read_via_bsatn(row)
551 }
552}
553
554impl From<StColumnRow> for ProductValue {
555 fn from(x: StColumnRow) -> Self {
556 to_product_value(&x)
557 }
558}
559
560impl From<StColumnRow> for ColumnSchema {
561 fn from(column: StColumnRow) -> Self {
562 Self {
563 table_id: column.table_id,
564 col_pos: column.col_pos,
565 col_name: column.col_name,
566 col_type: column.col_type.0,
567 }
568 }
569}
570
571#[derive(Debug, Clone, PartialEq, Eq, SpacetimeType)]
577#[sats(crate = spacetimedb_lib)]
578pub struct StIndexRow {
579 pub(crate) index_id: IndexId,
580 pub(crate) table_id: TableId,
581 pub(crate) index_name: Box<str>,
582 pub(crate) index_algorithm: StIndexAlgorithm,
583}
584
585#[derive(Debug, Clone, PartialEq, Eq, SpacetimeType)]
593#[sats(crate = spacetimedb_lib)]
594pub enum StIndexAlgorithm {
595 Unused(u128),
597
598 BTree { columns: ColList },
600
601 Direct { column: ColId },
603}
604
605impl From<IndexAlgorithm> for StIndexAlgorithm {
606 fn from(algorithm: IndexAlgorithm) -> Self {
607 match algorithm {
608 IndexAlgorithm::BTree(BTreeAlgorithm { columns }) => Self::BTree { columns },
609 IndexAlgorithm::Direct(DirectAlgorithm { column }) => Self::Direct { column },
610 algo => unreachable!("unexpected `{algo:?}`, did you add a new one?"),
611 }
612 }
613}
614
615impl From<StIndexAlgorithm> for IndexAlgorithm {
616 fn from(algorithm: StIndexAlgorithm) -> Self {
617 match algorithm {
618 StIndexAlgorithm::BTree { columns } => Self::BTree(BTreeAlgorithm { columns }),
619 StIndexAlgorithm::Direct { column } => Self::Direct(DirectAlgorithm { column }),
620 algo => unreachable!("unexpected `{algo:?}` in system table `st_indexes`"),
621 }
622 }
623}
624
625impl TryFrom<RowRef<'_>> for StIndexRow {
626 type Error = DatastoreError;
627 fn try_from(row: RowRef<'_>) -> Result<Self, DatastoreError> {
628 read_via_bsatn(row)
629 }
630}
631
632impl From<StIndexRow> for ProductValue {
633 fn from(x: StIndexRow) -> Self {
634 to_product_value(&x)
635 }
636}
637
638impl From<StIndexRow> for IndexSchema {
639 fn from(x: StIndexRow) -> Self {
640 Self {
641 index_id: x.index_id,
642 table_id: x.table_id,
643 index_name: x.index_name,
644 index_algorithm: x.index_algorithm.into(),
645 }
646 }
647}
648
649impl From<IndexSchema> for StIndexRow {
650 fn from(x: IndexSchema) -> Self {
651 Self {
652 index_id: x.index_id,
653 table_id: x.table_id,
654 index_name: x.index_name,
655 index_algorithm: x.index_algorithm.into(),
656 }
657 }
658}
659
660#[derive(Debug, Clone, PartialEq, Eq, SpacetimeType)]
666#[sats(crate = spacetimedb_lib)]
667pub struct StSequenceRow {
668 pub(crate) sequence_id: SequenceId,
669 pub(crate) sequence_name: Box<str>,
670 pub(crate) table_id: TableId,
671 pub(crate) col_pos: ColId,
672 pub(crate) increment: i128,
673 pub(crate) start: i128,
674 pub(crate) min_value: i128,
675 pub(crate) max_value: i128,
676 pub(crate) allocated: i128,
677}
678
679impl TryFrom<RowRef<'_>> for StSequenceRow {
680 type Error = DatastoreError;
681 fn try_from(row: RowRef<'_>) -> Result<Self, DatastoreError> {
682 read_via_bsatn(row)
683 }
684}
685
686impl From<StSequenceRow> for ProductValue {
687 fn from(x: StSequenceRow) -> Self {
688 to_product_value(&x)
689 }
690}
691
692impl From<StSequenceRow> for SequenceSchema {
693 fn from(sequence: StSequenceRow) -> Self {
694 Self {
695 sequence_id: sequence.sequence_id,
696 sequence_name: sequence.sequence_name,
697 table_id: sequence.table_id,
698 col_pos: sequence.col_pos,
699 start: sequence.start,
700 increment: sequence.increment,
701 min_value: sequence.min_value,
702 max_value: sequence.max_value,
703 allocated: sequence.allocated,
704 }
705 }
706}
707
708#[derive(Debug, Clone, PartialEq, Eq, SpacetimeType)]
714#[sats(crate = spacetimedb_lib)]
715pub struct StConstraintRow {
716 pub(crate) constraint_id: ConstraintId,
717 pub(crate) constraint_name: Box<str>,
718 pub(crate) table_id: TableId,
719 pub(crate) constraint_data: StConstraintData,
720}
721
722#[derive(Debug, Clone, PartialEq, Eq, SpacetimeType)]
730#[sats(crate = spacetimedb_lib)]
731pub enum StConstraintData {
732 Unused(u128),
734
735 Unique { columns: ColSet },
737}
738
739impl From<ConstraintData> for StConstraintData {
740 fn from(data: ConstraintData) -> Self {
741 match data {
742 ConstraintData::Unique(UniqueConstraintData { columns }) => StConstraintData::Unique { columns },
743 _ => unimplemented!(),
744 }
745 }
746}
747
748impl TryFrom<RowRef<'_>> for StConstraintRow {
749 type Error = DatastoreError;
750 fn try_from(row: RowRef<'_>) -> Result<Self, DatastoreError> {
751 read_via_bsatn(row)
752 }
753}
754
755impl From<StConstraintRow> for ProductValue {
756 fn from(x: StConstraintRow) -> Self {
757 to_product_value(&x)
758 }
759}
760
761impl From<StConstraintRow> for ConstraintSchema {
762 fn from(x: StConstraintRow) -> Self {
763 Self {
764 constraint_id: x.constraint_id,
765 constraint_name: x.constraint_name,
766 table_id: x.table_id,
767 data: match x.constraint_data {
768 StConstraintData::Unique { columns } => ConstraintData::Unique(UniqueConstraintData { columns }),
769 StConstraintData::Unused(_) => panic!("Someone put a forbidden variant in the system table!"),
770 },
771 }
772 }
773}
774
775#[derive(Debug, Clone, PartialEq, Eq, SpacetimeType)]
781#[sats(crate = spacetimedb_lib)]
782pub struct StRowLevelSecurityRow {
783 pub(crate) table_id: TableId,
784 pub(crate) sql: RawSql,
785}
786
787impl TryFrom<RowRef<'_>> for StRowLevelSecurityRow {
788 type Error = DatastoreError;
789 fn try_from(row: RowRef<'_>) -> Result<Self, DatastoreError> {
790 read_via_bsatn(row)
791 }
792}
793
794impl From<StRowLevelSecurityRow> for ProductValue {
795 fn from(x: StRowLevelSecurityRow) -> Self {
796 to_product_value(&x)
797 }
798}
799
800impl From<StRowLevelSecurityRow> for RowLevelSecuritySchema {
801 fn from(x: StRowLevelSecurityRow) -> Self {
802 Self {
803 table_id: x.table_id,
804 sql: x.sql,
805 }
806 }
807}
808#[derive(Clone, Copy, Debug, Eq, PartialEq)]
814pub struct ModuleKind(u8);
815
816impl ModuleKind {
817 pub const WASM: ModuleKind = ModuleKind(0);
819}
820
821impl From<crate::messages::control_db::HostType> for ModuleKind {
822 fn from(host_type: crate::messages::control_db::HostType) -> Self {
823 match host_type {
824 crate::messages::control_db::HostType::Wasm => Self::WASM,
825 }
826 }
827}
828
829impl_serialize!([] ModuleKind, (self, ser) => self.0.serialize(ser));
830impl_deserialize!([] ModuleKind, de => u8::deserialize(de).map(Self));
831impl_st!([] ModuleKind, AlgebraicType::U8);
832
833#[derive(Clone, Copy, Debug, Eq, PartialEq)]
835pub struct ConnectionIdViaU128(pub ConnectionId);
836impl_serialize!([] ConnectionIdViaU128, (self, ser) => self.0.to_u128().serialize(ser));
837impl_deserialize!([] ConnectionIdViaU128, de => <u128>::deserialize(de).map(ConnectionId::from_u128).map(ConnectionIdViaU128));
838impl_st!([] ConnectionIdViaU128, AlgebraicType::U128);
839impl From<ConnectionId> for ConnectionIdViaU128 {
840 fn from(id: ConnectionId) -> Self {
841 Self(id)
842 }
843}
844
845#[derive(Clone, Copy, Debug, Eq, PartialEq)]
847pub struct IdentityViaU256(pub Identity);
848impl_serialize!([] IdentityViaU256, (self, ser) => self.0.to_u256().serialize(ser));
849impl_deserialize!([] IdentityViaU256, de => <u256>::deserialize(de).map(Identity::from_u256).map(IdentityViaU256));
850impl_st!([] IdentityViaU256, AlgebraicType::U256);
851impl From<Identity> for IdentityViaU256 {
852 fn from(id: Identity) -> Self {
853 Self(id)
854 }
855}
856
857#[derive(Clone, Debug, Eq, PartialEq, SpacetimeType)]
872#[sats(crate = spacetimedb_lib)]
873pub struct StModuleRow {
874 pub(crate) database_identity: IdentityViaU256,
875 pub(crate) owner_identity: IdentityViaU256,
876 pub(crate) program_kind: ModuleKind,
877 pub(crate) program_hash: Hash,
878 pub(crate) program_bytes: Box<[u8]>,
879 pub(crate) module_version: Box<str>,
880}
881
882pub fn read_bytes_from_col(row: RowRef<'_>, col: impl StFields) -> Result<Box<[u8]>, DatastoreError> {
884 let bytes = row.read_col::<ArrayValue>(col.col_id())?;
885 if let ArrayValue::U8(bytes) = bytes {
886 Ok(bytes)
887 } else {
888 Err(InvalidFieldError {
889 name: Some(col.name()),
890 col_pos: col.col_id(),
891 }
892 .into())
893 }
894}
895
896pub fn read_identity_from_col(row: RowRef<'_>, col: impl StFields) -> Result<Identity, DatastoreError> {
900 Ok(Identity::from_u256(row.read_col(col.col_id())?))
901}
902
903pub fn read_hash_from_col(row: RowRef<'_>, col: impl StFields) -> Result<Hash, DatastoreError> {
907 Ok(Hash::from_u256(row.read_col(col.col_id())?))
908}
909
910impl TryFrom<RowRef<'_>> for StModuleRow {
911 type Error = DatastoreError;
912
913 fn try_from(row: RowRef<'_>) -> Result<Self, Self::Error> {
914 read_via_bsatn(row)
915 }
916}
917
918impl From<StModuleRow> for ProductValue {
919 fn from(row: StModuleRow) -> Self {
920 to_product_value(&row)
921 }
922}
923
924#[derive(Clone, Copy, Debug, Eq, PartialEq, SpacetimeType)]
930#[sats(crate = spacetimedb_lib)]
931pub struct StClientRow {
932 pub(crate) identity: IdentityViaU256,
933 pub(crate) connection_id: ConnectionIdViaU128,
934}
935
936impl From<StClientRow> for ProductValue {
937 fn from(var: StClientRow) -> Self {
938 to_product_value(&var)
939 }
940}
941impl From<&StClientRow> for ProductValue {
942 fn from(var: &StClientRow) -> Self {
943 to_product_value(var)
944 }
945}
946
947impl TryFrom<RowRef<'_>> for StClientRow {
948 type Error = DatastoreError;
949
950 fn try_from(row: RowRef<'_>) -> Result<Self, Self::Error> {
951 read_via_bsatn(row)
952 }
953}
954
955#[derive(Debug, Clone, SpacetimeType)]
961#[sats(crate = spacetimedb_lib)]
962pub struct StVarRow {
963 pub name: StVarName,
964 pub value: StVarValue,
965}
966
967impl From<StVarRow> for ProductValue {
968 fn from(var: StVarRow) -> Self {
969 to_product_value(&var)
970 }
971}
972
973impl From<StVarRow> for AlgebraicValue {
974 fn from(row: StVarRow) -> Self {
975 AlgebraicValue::Product(row.into())
976 }
977}
978
979pub const ST_VARNAME_ROW_LIMIT: &str = "row_limit";
983pub const ST_VARNAME_SLOW_QRY: &str = "slow_ad_hoc_query_ms";
985pub const ST_VARNAME_SLOW_SUB: &str = "slow_subscription_query_ms";
987pub const ST_VARNAME_SLOW_INC: &str = "slow_tx_update_ms";
989
990#[derive(Debug, Clone, Copy, PartialEq, Eq)]
992pub enum StVarName {
993 RowLimit,
994 SlowQryThreshold,
995 SlowSubThreshold,
996 SlowIncThreshold,
997}
998impl From<StVarName> for &'static str {
999 fn from(value: StVarName) -> Self {
1000 match value {
1001 StVarName::RowLimit => ST_VARNAME_ROW_LIMIT,
1002 StVarName::SlowQryThreshold => ST_VARNAME_SLOW_QRY,
1003 StVarName::SlowSubThreshold => ST_VARNAME_SLOW_SUB,
1004 StVarName::SlowIncThreshold => ST_VARNAME_SLOW_INC,
1005 }
1006 }
1007}
1008impl From<StVarName> for AlgebraicValue {
1009 fn from(value: StVarName) -> Self {
1010 let value: &'static str = value.into();
1011 AlgebraicValue::String(value.into())
1012 }
1013}
1014impl FromStr for StVarName {
1015 type Err = anyhow::Error;
1016
1017 fn from_str(s: &str) -> Result<Self, Self::Err> {
1018 match s {
1019 ST_VARNAME_ROW_LIMIT => Ok(StVarName::RowLimit),
1020 ST_VARNAME_SLOW_QRY => Ok(StVarName::SlowQryThreshold),
1021 ST_VARNAME_SLOW_SUB => Ok(StVarName::SlowSubThreshold),
1022 ST_VARNAME_SLOW_INC => Ok(StVarName::SlowIncThreshold),
1023 _ => Err(anyhow::anyhow!("Invalid system variable {}", s)),
1024 }
1025 }
1026}
1027impl_st!([] StVarName, AlgebraicType::String);
1028impl_serialize!([] StVarName, (self, ser) => <&'static str>::from(*self).serialize(ser));
1029impl<'de> Deserialize<'de> for StVarName {
1030 fn deserialize<D: spacetimedb_lib::de::Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
1031 let s = <&str>::deserialize(de)?;
1032 s.parse().map_err(D::Error::custom)
1033 }
1034}
1035
1036impl StVarName {
1037 pub fn type_of(&self) -> AlgebraicType {
1038 match self {
1039 StVarName::RowLimit
1040 | StVarName::SlowQryThreshold
1041 | StVarName::SlowSubThreshold
1042 | StVarName::SlowIncThreshold => AlgebraicType::U64,
1043 }
1044 }
1045}
1046
1047impl TryFrom<RowRef<'_>> for StVarRow {
1048 type Error = DatastoreError;
1049
1050 fn try_from(row: RowRef<'_>) -> Result<Self, Self::Error> {
1051 let col_pos = StVarFields::Value.col_id();
1053
1054 let invalid_value = InvalidFieldError {
1056 col_pos,
1057 name: Some(StVarFields::Value.name()),
1058 };
1059
1060 let name = row.read_col::<Box<str>>(StVarFields::Name.col_id())?;
1061 let name = StVarName::from_str(&name)?;
1062 match row.read_col::<AlgebraicValue>(col_pos)? {
1063 AlgebraicValue::Sum(sum) => Ok(StVarRow {
1064 name,
1065 value: sum.try_into().map_err(|_| invalid_value)?,
1066 }),
1067 _ => Err(invalid_value.into()),
1068 }
1069 }
1070}
1071
1072#[derive(Clone, Debug, Eq, PartialEq, SpacetimeType)]
1075#[sats(crate = spacetimedb_lib)]
1076pub struct StScheduledRow {
1077 pub(crate) schedule_id: ScheduleId,
1078 pub(crate) table_id: TableId,
1079 pub(crate) reducer_name: Box<str>,
1080 pub(crate) schedule_name: Box<str>,
1081 pub(crate) at_column: ColId,
1082}
1083
1084impl TryFrom<RowRef<'_>> for StScheduledRow {
1085 type Error = DatastoreError;
1086 fn try_from(row: RowRef<'_>) -> Result<Self, DatastoreError> {
1087 read_via_bsatn(row)
1088 }
1089}
1090
1091impl From<StScheduledRow> for ProductValue {
1092 fn from(x: StScheduledRow) -> Self {
1093 to_product_value(&x)
1094 }
1095}
1096
1097impl From<StScheduledRow> for ScheduleSchema {
1098 fn from(row: StScheduledRow) -> Self {
1099 Self {
1100 table_id: row.table_id,
1101 reducer_name: row.reducer_name,
1102 schedule_id: row.schedule_id,
1103 schedule_name: row.schedule_name,
1104 at_column: row.at_column,
1105 }
1106 }
1107}
1108
1109thread_local! {
1110 static READ_BUF: RefCell<Vec<u8>> = const { RefCell::new(Vec::new()) };
1111}
1112
1113pub(crate) fn with_sys_table_buf<R>(run: impl FnOnce(&mut Vec<u8>) -> R) -> R {
1115 READ_BUF.with_borrow_mut(|buf| {
1116 buf.clear();
1117 run(buf)
1118 })
1119}
1120
1121fn read_via_bsatn<T: DeserializeOwned>(row: RowRef<'_>) -> Result<T, DatastoreError> {
1123 with_sys_table_buf(|buf| Ok(row.read_via_bsatn::<T>(buf)?))
1124}
1125
1126fn to_product_value<T: Serialize>(value: &T) -> ProductValue {
1134 value_serialize(&value).into_product().expect("should be product")
1135}
1136
1137#[cfg(test)]
1138mod tests {
1139 use super::*;
1140
1141 #[test]
1142 fn test_sequences_within_reserved_range() {
1143 let mut num_tables = 0;
1144 let mut num_indexes = 0;
1145 let mut num_constraints = 0;
1146 let mut num_sequences = 0;
1147
1148 for table in system_tables() {
1149 num_tables += 1;
1150 num_indexes += table.indexes.len();
1151 num_constraints += table.constraints.len();
1152 num_sequences += table.sequences.len();
1153 }
1154
1155 assert!(
1156 num_tables <= ST_RESERVED_SEQUENCE_RANGE,
1157 "number of system tables exceeds reserved sequence range"
1158 );
1159 assert!(
1160 num_indexes <= ST_RESERVED_SEQUENCE_RANGE as usize,
1161 "number of system indexes exceeds reserved sequence range"
1162 );
1163 assert!(
1164 num_constraints <= ST_RESERVED_SEQUENCE_RANGE as usize,
1165 "number of system constraints exceeds reserved sequence range"
1166 );
1167 assert!(
1168 num_sequences <= ST_RESERVED_SEQUENCE_RANGE as usize,
1169 "number of system sequences exceeds reserved sequence range"
1170 );
1171 }
1172}