1use thiserror::Error;
2
3use crate::storage::page_cache::CacheError;
4
5#[derive(Debug, Clone, Error, miette::Diagnostic)]
6pub enum LimboError {
7 #[error("Corrupt database: {0}")]
8 Corrupt(String),
9 #[error("File is not a database")]
10 NotADB,
11 #[error("Internal error: {0}")]
12 InternalError(String),
13 #[error(transparent)]
14 CacheError(#[from] CacheError),
15 #[error("Database is full: {0}")]
16 DatabaseFull(String),
17 #[error("Parse error: {0}")]
18 ParseError(String),
19 #[error(transparent)]
20 #[diagnostic(transparent)]
21 LexerError(#[from] turso_parser::error::Error),
22 #[error("Conversion error: {0}")]
23 ConversionError(String),
24 #[error("Env variable error: {0}")]
25 EnvVarError(#[from] std::env::VarError),
26 #[error("Transaction error: {0}")]
27 TxError(String),
28 #[error(transparent)]
29 CompletionError(#[from] CompletionError),
30 #[error("Locking error: {0}")]
31 LockingError(String),
32 #[error("Parse error: {0}")]
33 ParseIntError(#[from] std::num::ParseIntError),
34 #[error("Parse error: {0}")]
35 ParseFloatError(#[from] std::num::ParseFloatError),
36 #[error("Parse error: {0}")]
37 InvalidDate(String),
38 #[error("Parse error: {0}")]
39 InvalidTime(String),
40 #[error("Modifier parsing error: {0}")]
41 InvalidModifier(String),
42 #[error("Invalid argument supplied: {0}")]
43 InvalidArgument(String),
44 #[error("Invalid formatter supplied: {0}")]
45 InvalidFormatter(String),
46 #[error("Runtime error: {0}")]
47 Constraint(String),
48 #[error("Runtime error: {0}")]
49 ForeignKeyConstraint(String),
52 #[error("Runtime error: {1}")]
53 Raise(turso_parser::ast::ResolveType, String),
54 #[error("RaiseIgnore")]
55 RaiseIgnore,
56 #[error("Extension error: {0}")]
57 ExtensionError(String),
58 #[error("Runtime error: integer overflow")]
59 IntegerOverflow,
60 #[error("Runtime error: string or blob too big")]
61 TooBig,
62 #[error("Runtime error: database table is locked")]
63 TableLocked,
64 #[error("Error: Resource is read-only")]
65 ReadOnly,
66 #[error("Database is busy")]
67 Busy,
68 #[error("{0} - SQL statements in progress")]
77 StatementsInProgress(&'static str),
78 #[error("interrupt")]
79 Interrupt,
80 #[error("Database snapshot is stale. You must rollback and retry the whole transaction.")]
81 BusySnapshot,
82 #[error("Conflict: {0}")]
83 Conflict(String),
84 #[error("Database schema changed")]
85 SchemaUpdated,
86 #[error("Database schema conflict")]
87 SchemaConflict,
88 #[error(
89 "Database is empty, header does not exist - page 1 should've been allocated before this"
90 )]
91 Page1NotAlloc,
92 #[error("Transaction terminated")]
93 TxTerminated,
94 #[error("Write-write conflict")]
95 WriteWriteConflict,
96 #[error("Commit dependency aborted")]
97 CommitDependencyAborted,
98 #[error("No such transaction ID: {0}")]
99 NoSuchTransactionID(String),
100 #[error("Null value")]
101 NullValue,
102 #[error("invalid column type")]
103 InvalidColumnType,
104 #[error("Invalid blob size, expected {0}")]
105 InvalidBlobSize(usize),
106 #[error("Planning error: {0}")]
107 PlanningError(String),
108 #[error("Checkpoint failed: {0}")]
109 CheckpointFailed(String),
110 #[error("Unsupported text encoding: {0}. Only UTF-8 is supported.")]
111 UnsupportedEncoding(String),
112 #[error("Out of memory")]
113 OutOfMemory,
114}
115
116impl From<crate::alloc::AllocError> for LimboError {
117 fn from(_: crate::alloc::AllocError) -> Self {
118 Self::OutOfMemory
119 }
120}
121
122impl From<crate::alloc::TryReserveError> for LimboError {
123 fn from(_: crate::alloc::TryReserveError) -> Self {
124 Self::OutOfMemory
125 }
126}
127
128#[cfg(not(nightly))]
129impl From<allocator_api2::collections::TryReserveError> for LimboError {
130 fn from(_: allocator_api2::collections::TryReserveError) -> Self {
131 Self::OutOfMemory
132 }
133}
134
135impl From<std::collections::TryReserveError> for LimboError {
136 fn from(_: std::collections::TryReserveError) -> Self {
137 Self::OutOfMemory
138 }
139}
140
141impl From<bumpalo::AllocErr> for LimboError {
142 fn from(_: bumpalo::AllocErr) -> Self {
143 Self::OutOfMemory
144 }
145}
146
147impl From<bumpalo::collections::CollectionAllocErr> for LimboError {
148 fn from(_: bumpalo::collections::CollectionAllocErr) -> Self {
149 Self::OutOfMemory
150 }
151}
152
153#[cfg(target_family = "unix")]
154impl From<rustix::io::Errno> for LimboError {
155 fn from(value: rustix::io::Errno) -> Self {
156 CompletionError::from(value).into()
157 }
158}
159
160#[cfg(all(target_os = "linux", clt_turso_feature = "io_uring"))]
161impl From<&'static str> for LimboError {
162 fn from(value: &'static str) -> Self {
163 CompletionError::UringIOError(value).into()
164 }
165}
166
167#[derive(Debug, Copy, Clone, PartialEq, Error)]
168pub enum CompletionError {
169 #[error("I/O error ({1}): {0}")]
170 IOError(std::io::ErrorKind, &'static str),
171 #[cfg(target_family = "unix")]
172 #[error("I/O error: {0}")]
173 RustixIOError(#[from] rustix::io::Errno),
174 #[cfg(all(target_os = "linux", clt_turso_feature = "io_uring"))]
175 #[error("I/O error: {0}")]
176 UringIOError(&'static str),
178 #[error("Completion was aborted")]
179 Aborted,
180 #[error("Decryption failed for page={page_idx}")]
181 DecryptionError { page_idx: usize },
182 #[error("I/O error: partial write")]
183 ShortWrite,
184 #[error("I/O error: short read on page {page_idx}: expected {expected} bytes, got {actual}")]
185 ShortRead {
186 page_idx: usize,
187 expected: usize,
188 actual: usize,
189 },
190 #[error("I/O error: short read on WAL frame at offset {offset}: expected {expected} bytes, got {actual}")]
191 ShortReadWalFrame {
192 offset: u64,
193 expected: usize,
194 actual: usize,
195 },
196 #[error("WAL frame page mismatch at frame {frame_id}: expected page {expected}, got {actual}")]
197 WalFramePageMismatch {
198 frame_id: u64,
199 expected: usize,
200 actual: u32,
201 },
202 #[error("Checksum mismatch on page {page_id}: expected {expected}, got {actual}")]
203 ChecksumMismatch {
204 page_id: usize,
205 expected: u64,
206 actual: u64,
207 },
208 #[error("tursodb not compiled with checksum feature")]
209 ChecksumNotEnabled,
210}
211
212pub fn io_error(e: std::io::Error, op: &'static str) -> LimboError {
214 LimboError::CompletionError(CompletionError::IOError(e.kind(), op))
215}
216
217#[cold]
218pub(crate) const fn cold_return<T>(v: T) -> T {
220 v
221}
222
223#[macro_export]
224macro_rules! bail_parse_error {
225 ($($arg:tt)*) => {
226 return $crate::error::cold_return(Err($crate::error::LimboError::ParseError(format!($($arg)*))))
227 };
228}
229
230#[macro_export]
231macro_rules! bail_corrupt_error {
232 ($($arg:tt)*) => {
233 return $crate::error::cold_return(Err($crate::error::LimboError::Corrupt(format!($($arg)*))))
234 };
235}
236
237#[macro_export]
241macro_rules! slice_in_bounds_or_corrupt {
242 ($buf:expr, $range:expr) => {
243 $buf.get($range).ok_or_else(|| {
244 $crate::error::cold_return($crate::error::LimboError::Corrupt(format!(
245 "range {:?} out of bounds for buffer size {}",
246 $range,
247 $buf.len()
248 )))
249 })?
250 };
251}
252
253#[macro_export]
258macro_rules! assert_or_bail_corrupt {
259 ($cond:expr, $($arg:tt)*) => {
260 if !($cond) {
261 $crate::bail_corrupt_error!($($arg)*);
262 }
263 };
264}
265
266#[macro_export]
267macro_rules! bail_constraint_error {
268 ($($arg:tt)*) => {
269 return $crate::error::cold_return(Err($crate::error::LimboError::Constraint(format!($($arg)*))))
270 };
271}
272
273impl From<turso_ext::ResultCode> for LimboError {
274 fn from(err: turso_ext::ResultCode) -> Self {
275 cold_return(LimboError::ExtensionError(err.to_string()))
276 }
277}
278
279pub const SQLITE_ERROR: usize = 1;
280pub const SQLITE_CONSTRAINT: usize = 19;
281pub const SQLITE_CONSTRAINT_CHECK: usize = SQLITE_CONSTRAINT | (1 << 8);
282pub const SQLITE_CONSTRAINT_PRIMARYKEY: usize = SQLITE_CONSTRAINT | (6 << 8);
283#[allow(dead_code)]
284pub const SQLITE_CONSTRAINT_FOREIGNKEY: usize = SQLITE_CONSTRAINT | (3 << 8);
285pub const SQLITE_CONSTRAINT_NOTNULL: usize = SQLITE_CONSTRAINT | (5 << 8);
286pub const SQLITE_CONSTRAINT_TRIGGER: usize = SQLITE_CONSTRAINT | (7 << 8);
287pub const SQLITE_FULL: usize = 13; pub const SQLITE_CONSTRAINT_UNIQUE: usize = 2067;
289#[allow(dead_code)]
295pub const SQLITE_BUSY: usize = 5;