Files
turso/core/error.rs

171 lines
5.3 KiB
Rust

use thiserror::Error;
use crate::storage::page_cache::CacheError;
#[derive(Debug, Clone, Error, miette::Diagnostic)]
pub enum LimboError {
#[error("Corrupt database: {0}")]
Corrupt(String),
#[error("File is not a database")]
NotADB,
#[error("Internal error: {0}")]
InternalError(String),
#[error(transparent)]
CacheError(#[from] CacheError),
#[error("Database is full: {0}")]
DatabaseFull(String),
#[error("Parse error: {0}")]
ParseError(String),
#[error(transparent)]
#[diagnostic(transparent)]
LexerError(#[from] turso_parser::error::Error),
#[error("Conversion error: {0}")]
ConversionError(String),
#[error("Env variable error: {0}")]
EnvVarError(#[from] std::env::VarError),
#[error("Transaction error: {0}")]
TxError(String),
#[error(transparent)]
CompletionError(#[from] CompletionError),
#[error("Locking error: {0}")]
LockingError(String),
#[error("Parse error: {0}")]
ParseIntError(#[from] std::num::ParseIntError),
#[error("Parse error: {0}")]
ParseFloatError(#[from] std::num::ParseFloatError),
#[error("Parse error: {0}")]
InvalidDate(String),
#[error("Parse error: {0}")]
InvalidTime(String),
#[error("Modifier parsing error: {0}")]
InvalidModifier(String),
#[error("Invalid argument supplied: {0}")]
InvalidArgument(String),
#[error("Invalid formatter supplied: {0}")]
InvalidFormatter(String),
#[error("Runtime error: {0}")]
Constraint(String),
#[error("Extension error: {0}")]
ExtensionError(String),
#[error("Runtime error: integer overflow")]
IntegerOverflow,
#[error("Runtime error: database table is locked")]
TableLocked,
#[error("Error: Resource is read-only")]
ReadOnly,
#[error("Database is busy")]
Busy,
#[error("Conflict: {0}")]
Conflict(String),
#[error("Database schema changed")]
SchemaUpdated,
#[error(
"Database is empty, header does not exist - page 1 should've been allocated before this"
)]
Page1NotAlloc,
#[error("Transaction terminated")]
TxTerminated,
#[error("Write-write conflict")]
WriteWriteConflict,
#[error("No such transaction ID: {0}")]
NoSuchTransactionID(String),
#[error("Null value")]
NullValue,
#[error("invalid column type")]
InvalidColumnType,
#[error("Invalid blob size, expected {0}")]
InvalidBlobSize(usize),
#[error("Planning error: {0}")]
PlanningError(String),
}
// We only propagate the error kind so we can avoid string allocation in hot path and copying/cloning enums is cheaper
impl From<std::io::Error> for LimboError {
fn from(value: std::io::Error) -> Self {
Self::CompletionError(CompletionError::IOError(value.kind()))
}
}
#[cfg(target_family = "unix")]
impl From<rustix::io::Errno> for LimboError {
fn from(value: rustix::io::Errno) -> Self {
CompletionError::from(value).into()
}
}
#[cfg(all(target_os = "linux", feature = "io_uring"))]
impl From<&'static str> for LimboError {
fn from(value: &'static str) -> Self {
CompletionError::UringIOError(value).into()
}
}
// We only propagate the error kind
impl From<std::io::Error> for CompletionError {
fn from(value: std::io::Error) -> Self {
CompletionError::IOError(value.kind())
}
}
#[derive(Debug, Copy, Clone, PartialEq, Error)]
pub enum CompletionError {
#[error("I/O error: {0}")]
IOError(std::io::ErrorKind),
#[cfg(target_family = "unix")]
#[error("I/O error: {0}")]
RustixIOError(#[from] rustix::io::Errno),
#[cfg(all(target_os = "linux", feature = "io_uring"))]
#[error("I/O error: {0}")]
// TODO: if needed create an enum for IO Uring errors so that we don't have to pass strings around
UringIOError(&'static str),
#[error("Completion was aborted")]
Aborted,
#[error("Decryption failed for page={page_idx}")]
DecryptionError { page_idx: usize },
#[error("I/O error: partial write")]
ShortWrite,
#[error("Checksum mismatch on page {page_id}: expected {expected}, got {actual}")]
ChecksumMismatch {
page_id: usize,
expected: u64,
actual: u64,
},
#[error("tursodb not compiled with checksum feature")]
ChecksumNotEnabled,
}
#[macro_export]
macro_rules! bail_parse_error {
($($arg:tt)*) => {
return Err($crate::error::LimboError::ParseError(format!($($arg)*)))
};
}
#[macro_export]
macro_rules! bail_corrupt_error {
($($arg:tt)*) => {
return Err($crate::error::LimboError::Corrupt(format!($($arg)*)))
};
}
#[macro_export]
macro_rules! bail_constraint_error {
($($arg:tt)*) => {
return Err($crate::error::LimboError::Constraint(format!($($arg)*)))
};
}
impl From<turso_ext::ResultCode> for LimboError {
fn from(err: turso_ext::ResultCode) -> Self {
LimboError::ExtensionError(err.to_string())
}
}
pub const SQLITE_CONSTRAINT: usize = 19;
pub const SQLITE_CONSTRAINT_PRIMARYKEY: usize = SQLITE_CONSTRAINT | (6 << 8);
#[allow(dead_code)]
pub const SQLITE_CONSTRAINT_FOREIGNKEY: usize = SQLITE_CONSTRAINT | (7 << 8);
pub const SQLITE_CONSTRAINT_NOTNULL: usize = SQLITE_CONSTRAINT | (5 << 8);
pub const SQLITE_FULL: usize = 13; // we want this in autoincrement - incase if user inserts max allowed int
pub const SQLITE_CONSTRAINT_UNIQUE: usize = 2067;