spacetimedb_datastore/
error.rs

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