1#![allow(dead_code)]
12
13use thiserror::Error;
14
15use std::collections::{BTreeMap, BTreeSet};
16
17use rusqlite;
18
19use edn::entities::TempId;
20
21use core_traits::{Entid, KnownEntid, TypedValue, ValueType};
22
23pub type Result<T> = ::std::result::Result<T, DbError>;
24
25#[macro_export]
28macro_rules! bail {
29 ($e:expr) => {
30 return ::std::result::Result::Err(::std::convert::From::from($e))
31 };
32}
33
34pub trait ResultExt<T> {
38 fn context(self, kind: DbErrorKind) -> Result<T>;
39}
40
41impl<T, E> ResultExt<T> for ::std::result::Result<T, E> {
42 fn context(self, kind: DbErrorKind) -> Result<T> {
43 self.map_err(|_| DbError::from(kind))
44 }
45}
46
47#[derive(Clone, Debug, Eq, PartialEq)]
49pub enum CardinalityConflict {
50 CardinalityOneAddConflict {
52 e: Entid,
53 a: Entid,
54 vs: BTreeSet<TypedValue>,
55 },
56
57 AddRetractConflict {
59 e: Entid,
60 a: Entid,
61 vs: BTreeSet<TypedValue>,
62 },
63}
64
65#[derive(Clone, Debug, Eq, PartialEq)]
67pub enum SchemaConstraintViolation {
68 ConflictingUpserts {
71 conflicting_upserts: BTreeMap<TempId, BTreeSet<KnownEntid>>,
78 },
79
80 TypeDisagreements {
82 conflicting_datoms: BTreeMap<(Entid, Entid, TypedValue), ValueType>,
84 },
85
86 CardinalityConflicts { conflicts: Vec<CardinalityConflict> },
88}
89
90impl ::std::fmt::Display for SchemaConstraintViolation {
91 fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
92 use self::SchemaConstraintViolation::*;
93 match self {
94 ConflictingUpserts {
95 ref conflicting_upserts,
96 } => {
97 writeln!(f, "conflicting upserts:")?;
98 for (tempid, entids) in conflicting_upserts {
99 writeln!(f, " tempid {:?} upserts to {:?}", tempid, entids)?;
100 }
101 Ok(())
102 }
103 TypeDisagreements {
104 ref conflicting_datoms,
105 } => {
106 writeln!(f, "type disagreements:")?;
107 for (datom, expected_type) in conflicting_datoms {
108 writeln!(
109 f,
110 " expected value of type {} but got datom [{} {} {:?}]",
111 expected_type, datom.0, datom.1, datom.2
112 )?;
113 }
114 Ok(())
115 }
116 CardinalityConflicts { ref conflicts } => {
117 writeln!(f, "cardinality conflicts:")?;
118 for conflict in conflicts {
119 writeln!(f, " {:?}", conflict)?;
120 }
121 Ok(())
122 }
123 }
124 }
125}
126
127#[derive(Copy, Clone, Eq, PartialEq, Debug)]
128pub enum InputError {
129 BadDbId,
131
132 BadEntityPlace,
135}
136
137impl ::std::fmt::Display for InputError {
138 fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
139 use self::InputError::*;
140 match self {
141 BadDbId => {
142 writeln!(f, ":db/id in map notation must either not be present or be an entid, an ident, or a tempid")
143 }
144 BadEntityPlace => {
145 writeln!(f, "cannot convert value place into entity place")
146 }
147 }
148 }
149}
150
151#[derive(Clone, Debug, Error)]
152#[error(transparent)]
153pub struct DbError {
154 inner: DbErrorKind,
155}
156
157impl DbError {
158 pub fn kind(&self) -> DbErrorKind {
159 self.inner.clone()
160 }
161}
162
163impl From<DbErrorKind> for DbError {
164 fn from(kind: DbErrorKind) -> Self {
165 DbError { inner: kind }
166 }
167}
168
169impl From<sql_traits::conn::SqlError> for DbError {
170 fn from(error: sql_traits::conn::SqlError) -> Self {
171 DbError {
172 inner: DbErrorKind::RusqliteError(error.message),
173 }
174 }
175}
176
177impl From<rusqlite::Error> for DbError {
178 fn from(error: rusqlite::Error) -> Self {
179 DbError {
180 inner: DbErrorKind::RusqliteError(error.to_string()),
181 }
182 }
183}
184
185#[derive(Clone, PartialEq, Debug, Error)]
186pub enum DbErrorKind {
187 #[error("not yet implemented: {0}")]
189 NotYetImplemented(String),
190
191 #[error("value '{0}' is not the expected Mentat value type {1:?}")]
193 BadValuePair(String, ValueType),
194
195 #[error("bad SQL (value_type_tag, value) pair: ({0:?}, {1:?})")]
198 BadSQLValuePair(sql_traits::conn::SqlValue, i32),
199
200 #[error("bad bootstrap definition: {0}")]
207 BadBootstrapDefinition(String),
208
209 #[error("bad schema assertion: {0}")]
211 BadSchemaAssertion(String),
212
213 #[error("no entid found for ident: {0}")]
215 UnrecognizedIdent(String),
216
217 #[error("no ident found for entid: {0}")]
219 UnrecognizedEntid(Entid),
220
221 #[error("entid not allocated: {0}")]
223 UnallocatedEntid(Entid),
224
225 #[error("unknown attribute for entid: {0}")]
226 UnknownAttribute(Entid),
227
228 #[error("cannot reverse-cache non-unique attribute: {0}")]
229 CannotCacheNonUniqueAttributeInReverse(Entid),
230
231 #[error("schema alteration failed: {0}")]
232 SchemaAlterationFailed(String),
233
234 #[error("schema constraint violation: {0}")]
236 SchemaConstraintViolation(SchemaConstraintViolation),
237
238 #[error("transaction input error: {0}")]
241 InputError(InputError),
242
243 #[error(
244 "Cannot transact a fulltext assertion with a typed value that is not :db/valueType :db.type/string"
245 )]
246 WrongTypeValueForFtsAssertion,
247
248 #[error("could not update a cache")]
250 CacheUpdateFailed,
251
252 #[error("Could not set_user_version")]
253 CouldNotSetVersionPragma,
254
255 #[error("Could not get_user_version")]
256 CouldNotGetVersionPragma,
257
258 #[error("Could not search!")]
259 CouldNotSearch,
260
261 #[error("Could not insert transaction: failed to add datoms not already present")]
262 TxInsertFailedToAddMissingDatoms,
263
264 #[error("Could not insert transaction: failed to retract datoms already present")]
265 TxInsertFailedToRetractDatoms,
266
267 #[error("Could not update datoms: failed to retract datoms already present")]
268 DatomsUpdateFailedToRetract,
269
270 #[error("Could not update datoms: failed to add datoms not already present")]
271 DatomsUpdateFailedToAdd,
272
273 #[error("Failed to create temporary tables")]
274 FailedToCreateTempTables,
275
276 #[error("Could not insert non-fts one statements into temporary search table!")]
277 NonFtsInsertionIntoTempSearchTableFailed,
278
279 #[error("Could not insert fts values into fts table!")]
280 FtsInsertionFailed,
281
282 #[error("Could not insert FTS statements into temporary search table!")]
283 FtsInsertionIntoTempSearchTableFailed,
284
285 #[error("Could not drop FTS search ids!")]
286 FtsFailedToDropSearchIds,
287
288 #[error("Could not update partition map")]
289 FailedToUpdatePartitionMap,
290
291 #[error("Can't operate over mixed timelines")]
292 TimelinesMixed,
293
294 #[error("Can't move transactions to a non-empty timeline")]
295 TimelinesMoveToNonEmpty,
296
297 #[error("Supplied an invalid transaction range")]
298 TimelinesInvalidRange,
299
300 #[error("compare-and-swap failed for [{e} {a}]: expected {}, got {}", .expected.as_deref().unwrap_or("nil"), .actual.as_deref().unwrap_or("nil"))]
305 CasMismatch {
306 e: i64,
307 a: i64,
308 expected: Option<String>,
309 actual: Option<String>,
310 },
311
312 #[error("SQL error: {0}")]
315 RusqliteError(String),
316}