spacetimedb/db/datastore/
error.rs

1use super::system_tables::SystemTable;
2use enum_as_inner::EnumAsInner;
3use spacetimedb_lib::buffer::DecodeError;
4use spacetimedb_lib::{
5    db::{
6        error::LibError,
7        raw_def::{v9::RawSql, RawIndexDefV8},
8    },
9    AlgebraicType, AlgebraicValue, ProductValue,
10};
11use spacetimedb_primitives::{ColId, ColList, IndexId, SequenceId, TableId};
12use spacetimedb_sats::{product_value::InvalidFieldError, satn::Satn};
13use spacetimedb_snapshot::SnapshotError;
14use spacetimedb_table::{
15    bflatn_to, read_column,
16    table::{self, ReadViaBsatnError, UniqueConstraintViolation},
17};
18use thiserror::Error;
19
20#[derive(Error, Debug, EnumAsInner)]
21pub enum DatastoreError {
22    #[error("LibError: {0}")]
23    Lib(#[from] LibError),
24    #[error("TableError: {0}")]
25    Table(#[from] TableError),
26    #[error("IndexError: {0}")]
27    Index(#[from] IndexError),
28    #[error("SequenceError: {0}")]
29    Sequence(#[from] SequenceError),
30    #[error(transparent)]
31    // Box the inner [`SnapshotError`] to keep Clippy quiet about large `Err` variants.
32    Snapshot(#[from] Box<SnapshotError>),
33    // TODO(cloutiertyler): should this be a TableError? I couldn't get it to compile
34    #[error("Error reading a value from a table through BSATN: {0}")]
35    ReadViaBsatnError(#[from] ReadViaBsatnError),
36    #[error(transparent)]
37    Other(#[from] anyhow::Error),
38}
39
40#[derive(Error, Debug, EnumAsInner)]
41pub enum TableError {
42    #[error("Table with name `{0}` start with 'st_' and that is reserved for internal system tables.")]
43    System(Box<str>),
44    #[error("Table with name `{0}` already exists.")]
45    Exist(String),
46    #[error("Table with name `{0}` not found.")]
47    NotFound(String),
48    #[error("Table with ID `{1}` not found in `{0}`.")]
49    IdNotFound(SystemTable, u32),
50    #[error("Sql `{1}` not found in `{0}`.")]
51    RawSqlNotFound(SystemTable, RawSql),
52    #[error("Table with ID `{0}` not found in `TxState`.")]
53    IdNotFoundState(TableId),
54    #[error("Column `{0}.{1}` is missing a name")]
55    ColumnWithoutName(String, ColId),
56    #[error("schema_for_table: Table has invalid schema: {0} Err: {1}")]
57    InvalidSchema(TableId, LibError),
58    #[error("Row has invalid row type for table: {0} Err: {1}", table_id, row.to_satn())]
59    RowInvalidType { table_id: TableId, row: ProductValue },
60    #[error("failed to decode row in table")]
61    RowDecodeError(DecodeError),
62    #[error("Column with name `{0}` already exists")]
63    DuplicateColumnName(String),
64    #[error("Column `{0}` not found")]
65    ColumnNotFound(ColId),
66    #[error(
67        "DecodeError for field `{0}.{1}`, expect `{2}` but found `{3}`",
68        table,
69        field,
70        expect,
71        found
72    )]
73    DecodeField {
74        table: String,
75        field: Box<str>,
76        expect: String,
77        found: String,
78    },
79    #[error(transparent)]
80    Bflatn(#[from] bflatn_to::Error),
81    #[error(transparent)]
82    Duplicate(#[from] table::DuplicateError),
83    #[error(transparent)]
84    ReadColTypeError(#[from] read_column::TypeError),
85}
86
87#[derive(Error, Debug, PartialEq, Eq)]
88pub enum IndexError {
89    #[error("Index not found: {0:?}")]
90    NotFound(IndexId),
91    #[error("Column not found: {0:?}")]
92    ColumnNotFound(RawIndexDefV8),
93    #[error(transparent)]
94    UniqueConstraintViolation(#[from] UniqueConstraintViolation),
95    #[error("Attempt to define a index with more than 1 auto_inc column: Table: {0:?}, Columns: {1:?}")]
96    OneAutoInc(TableId, Vec<String>),
97    #[error("Could not decode arguments to index scan")]
98    Decode(DecodeError),
99    #[error("Index was not unique: {0:?}")]
100    NotUnique(IndexId),
101    #[error("Key {1:?} was not found in index {0:?}")]
102    KeyNotFound(IndexId, AlgebraicValue),
103}
104
105#[derive(Error, Debug, PartialEq, Eq)]
106pub enum SequenceError {
107    #[error("Sequence with name `{0}` already exists.")]
108    Exist(String),
109    #[error("Sequence `{0}`: The increment is 0, and this means the sequence can't advance.")]
110    IncrementIsZero(String),
111    #[error("Sequence `{0}`: The min_value {1} must < max_value {2}.")]
112    MinMax(String, i128, i128),
113    #[error("Sequence `{0}`: The start value {1} must be >= min_value {2}.")]
114    MinStart(String, i128, i128),
115    #[error("Sequence `{0}`: The start value {1} must be <= min_value {2}.")]
116    MaxStart(String, i128, i128),
117    #[error("Sequence `{0}` failed to decode value from Sled (not a u128).")]
118    SequenceValue(String),
119    #[error("Sequence ID `{0}` not found.")]
120    NotFound(SequenceId),
121    #[error("Sequence applied to a non-integer field. Column `{col}` is of type {{found.to_sats()}}.")]
122    NotInteger { col: String, found: AlgebraicType },
123    #[error("Sequence ID `{0}` still had no values left after allocation.")]
124    UnableToAllocate(SequenceId),
125    #[error("Autoinc constraint on table {0:?} spans more than one column: {1:?}")]
126    MultiColumnAutoInc(TableId, ColList),
127}
128
129impl From<InvalidFieldError> for DatastoreError {
130    fn from(value: InvalidFieldError) -> Self {
131        LibError::from(value).into()
132    }
133}
134
135impl From<spacetimedb_table::read_column::TypeError> for DatastoreError {
136    fn from(err: spacetimedb_table::read_column::TypeError) -> Self {
137        TableError::from(err).into()
138    }
139}
140
141impl From<table::InsertError> for DatastoreError {
142    fn from(err: table::InsertError) -> Self {
143        match err {
144            table::InsertError::Duplicate(e) => TableError::from(e).into(),
145            table::InsertError::Bflatn(e) => TableError::from(e).into(),
146            table::InsertError::IndexError(e) => IndexError::from(e).into(),
147        }
148    }
149}
150
151impl From<bflatn_to::Error> for DatastoreError {
152    fn from(err: bflatn_to::Error) -> Self {
153        Self::Table(err.into())
154    }
155}
156
157impl From<SnapshotError> for DatastoreError {
158    fn from(e: SnapshotError) -> Self {
159        DatastoreError::Snapshot(Box::new(e))
160    }
161}