use std::ffi::CStr;
use libsqlite3_sys::{self, sqlite3};
use crate::sqlite::ffi;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum PrimaryErrCode {
Error,
Internal,
Perm,
Abort,
Busy,
Locked,
NoMem,
ReadOnly,
Interrupt,
IoErr,
Corrupt,
NotFound,
Full,
CantOpen,
Protocol,
Empty,
Schema,
TooBig,
Constraint,
Mismatch,
Misuse,
NoLfs,
Auth,
Format,
Range,
NotADB,
Notice,
Warning,
Unknown(u32),
}
impl PrimaryErrCode {
fn from_code(code: i32) -> Self {
match code & 255 {
libsqlite3_sys::SQLITE_ERROR => Self::Error,
libsqlite3_sys::SQLITE_INTERNAL => Self::Internal,
libsqlite3_sys::SQLITE_PERM => Self::Perm,
libsqlite3_sys::SQLITE_ABORT => Self::Abort,
libsqlite3_sys::SQLITE_BUSY => Self::Busy,
libsqlite3_sys::SQLITE_LOCKED => Self::Locked,
libsqlite3_sys::SQLITE_NOMEM => Self::NoMem,
libsqlite3_sys::SQLITE_READONLY => Self::ReadOnly,
libsqlite3_sys::SQLITE_INTERRUPT => Self::Interrupt,
libsqlite3_sys::SQLITE_IOERR => Self::IoErr,
libsqlite3_sys::SQLITE_CORRUPT => Self::Corrupt,
libsqlite3_sys::SQLITE_NOTFOUND => Self::NotFound,
libsqlite3_sys::SQLITE_FULL => Self::Full,
libsqlite3_sys::SQLITE_CANTOPEN => Self::CantOpen,
libsqlite3_sys::SQLITE_PROTOCOL => Self::Protocol,
libsqlite3_sys::SQLITE_EMPTY => Self::Empty,
libsqlite3_sys::SQLITE_SCHEMA => Self::Schema,
libsqlite3_sys::SQLITE_TOOBIG => Self::TooBig,
libsqlite3_sys::SQLITE_CONSTRAINT => Self::Constraint,
libsqlite3_sys::SQLITE_MISMATCH => Self::Mismatch,
libsqlite3_sys::SQLITE_MISUSE => Self::Misuse,
libsqlite3_sys::SQLITE_NOLFS => Self::NoLfs,
libsqlite3_sys::SQLITE_AUTH => Self::Auth,
libsqlite3_sys::SQLITE_FORMAT => Self::Format,
libsqlite3_sys::SQLITE_RANGE => Self::Range,
libsqlite3_sys::SQLITE_NOTADB => Self::NotADB,
libsqlite3_sys::SQLITE_NOTICE => Self::Notice,
libsqlite3_sys::SQLITE_WARNING => Self::Warning,
_ => Self::Unknown(code as u32),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum ExtendedErrCode {
ErrorMissingCollseq,
ErrorRetry,
ErrorSnapshot,
IOErrRead,
IOErrShortRead,
IOErrWrite,
IOErrFsync,
IOErrDirFsync,
IOErrTruncate,
IOErrFstat,
IOErrUnlock,
IOErrRdlock,
IOErrDelete,
IOErrBlocked,
IOErrNoMem,
IOErrAccess,
IOErrCheckReservedLock,
IOErrLock,
IOErrClose,
IOErrDirClose,
IOErrShmopen,
IOErrShmsize,
IOErrShmlock,
IOErrShmmap,
IOErrSeek,
IOErrDeleteNoent,
IOErrMmap,
IOErrGetTempPath,
IOErrConvPath,
IOErrVnode,
IOErrAuth,
IOErrBeginAtomic,
IOErrCommitAtomic,
IOErrRollbackAtomic,
IOErrData,
IOErrCorruptFs,
LockedSharedCache,
LockedVTab,
BusyRecovery,
BusySnapshot,
BusyTimeout,
CantOpenNoTempDir,
CantOpenIsDir,
CantOpenFullPath,
CantOpenConvPath,
CantOpenDirtyWal,
CantOpenSymlink,
CorruptVTab,
CorruptSequence,
CorruptIndex,
ReadOnlyRecovery,
ReadOnlyCantLock,
ReadOnlyRollback,
ReadOnlyDbMoved,
ReadOnlyCantInit,
ReadOnlyDirectory,
AbortRollback,
ConstraintCheck,
ConstraintCommitHook,
ConstraintForeignKey,
ConstraintFunction,
ConstraintNotNull,
ConstraintPrimaryKey,
ConstraintTrigger,
ConstraintUnique,
ConstraintVTab,
ConstraintRowId,
ConstraintPinned,
ConstraintDataType,
NoticeRecoverWal,
NoticeRecoverRollback,
WarningAutoIndex,
AuthUser,
OkLoadPermanently,
OkSymlink,
Unknown(u32),
}
impl ExtendedErrCode {
fn from_code(code: i32) -> Self {
match code {
libsqlite3_sys::SQLITE_ERROR_MISSING_COLLSEQ => Self::ErrorMissingCollseq,
libsqlite3_sys::SQLITE_ERROR_RETRY => Self::ErrorRetry,
libsqlite3_sys::SQLITE_ERROR_SNAPSHOT => Self::ErrorSnapshot,
libsqlite3_sys::SQLITE_IOERR_READ => Self::IOErrRead,
libsqlite3_sys::SQLITE_IOERR_SHORT_READ => Self::IOErrShortRead,
libsqlite3_sys::SQLITE_IOERR_WRITE => Self::IOErrWrite,
libsqlite3_sys::SQLITE_IOERR_FSYNC => Self::IOErrFsync,
libsqlite3_sys::SQLITE_IOERR_DIR_FSYNC => Self::IOErrDirFsync,
libsqlite3_sys::SQLITE_IOERR_TRUNCATE => Self::IOErrTruncate,
libsqlite3_sys::SQLITE_IOERR_FSTAT => Self::IOErrFstat,
libsqlite3_sys::SQLITE_IOERR_UNLOCK => Self::IOErrUnlock,
libsqlite3_sys::SQLITE_IOERR_RDLOCK => Self::IOErrRdlock,
libsqlite3_sys::SQLITE_IOERR_DELETE => Self::IOErrDelete,
libsqlite3_sys::SQLITE_IOERR_BLOCKED => Self::IOErrBlocked,
libsqlite3_sys::SQLITE_IOERR_NOMEM => Self::IOErrNoMem,
libsqlite3_sys::SQLITE_IOERR_ACCESS => Self::IOErrAccess,
libsqlite3_sys::SQLITE_IOERR_CHECKRESERVEDLOCK => Self::IOErrCheckReservedLock,
libsqlite3_sys::SQLITE_IOERR_LOCK => Self::IOErrLock,
libsqlite3_sys::SQLITE_IOERR_CLOSE => Self::IOErrClose,
libsqlite3_sys::SQLITE_IOERR_DIR_CLOSE => Self::IOErrDirClose,
libsqlite3_sys::SQLITE_IOERR_SHMOPEN => Self::IOErrShmopen,
libsqlite3_sys::SQLITE_IOERR_SHMSIZE => Self::IOErrShmsize,
libsqlite3_sys::SQLITE_IOERR_SHMLOCK => Self::IOErrShmlock,
libsqlite3_sys::SQLITE_IOERR_SHMMAP => Self::IOErrShmmap,
libsqlite3_sys::SQLITE_IOERR_SEEK => Self::IOErrSeek,
libsqlite3_sys::SQLITE_IOERR_DELETE_NOENT => Self::IOErrDeleteNoent,
libsqlite3_sys::SQLITE_IOERR_MMAP => Self::IOErrMmap,
libsqlite3_sys::SQLITE_IOERR_GETTEMPPATH => Self::IOErrGetTempPath,
libsqlite3_sys::SQLITE_IOERR_CONVPATH => Self::IOErrConvPath,
libsqlite3_sys::SQLITE_IOERR_VNODE => Self::IOErrVnode,
libsqlite3_sys::SQLITE_IOERR_AUTH => Self::IOErrAuth,
libsqlite3_sys::SQLITE_IOERR_BEGIN_ATOMIC => Self::IOErrBeginAtomic,
libsqlite3_sys::SQLITE_IOERR_COMMIT_ATOMIC => Self::IOErrCommitAtomic,
libsqlite3_sys::SQLITE_IOERR_ROLLBACK_ATOMIC => Self::IOErrRollbackAtomic,
libsqlite3_sys::SQLITE_IOERR_DATA => Self::IOErrData,
libsqlite3_sys::SQLITE_IOERR_CORRUPTFS => Self::IOErrCorruptFs,
libsqlite3_sys::SQLITE_LOCKED_SHAREDCACHE => Self::LockedSharedCache,
libsqlite3_sys::SQLITE_LOCKED_VTAB => Self::LockedVTab,
libsqlite3_sys::SQLITE_BUSY_RECOVERY => Self::BusyRecovery,
libsqlite3_sys::SQLITE_BUSY_SNAPSHOT => Self::BusySnapshot,
libsqlite3_sys::SQLITE_BUSY_TIMEOUT => Self::BusyTimeout,
libsqlite3_sys::SQLITE_CANTOPEN_NOTEMPDIR => Self::CantOpenNoTempDir,
libsqlite3_sys::SQLITE_CANTOPEN_ISDIR => Self::CantOpenIsDir,
libsqlite3_sys::SQLITE_CANTOPEN_FULLPATH => Self::CantOpenFullPath,
libsqlite3_sys::SQLITE_CANTOPEN_CONVPATH => Self::CantOpenConvPath,
libsqlite3_sys::SQLITE_CANTOPEN_DIRTYWAL => Self::CantOpenDirtyWal,
libsqlite3_sys::SQLITE_CANTOPEN_SYMLINK => Self::CantOpenSymlink,
libsqlite3_sys::SQLITE_CORRUPT_VTAB => Self::CorruptVTab,
libsqlite3_sys::SQLITE_CORRUPT_SEQUENCE => Self::CorruptSequence,
libsqlite3_sys::SQLITE_CORRUPT_INDEX => Self::CorruptIndex,
libsqlite3_sys::SQLITE_READONLY_RECOVERY => Self::ReadOnlyRecovery,
libsqlite3_sys::SQLITE_READONLY_CANTLOCK => Self::ReadOnlyCantLock,
libsqlite3_sys::SQLITE_READONLY_ROLLBACK => Self::ReadOnlyRollback,
libsqlite3_sys::SQLITE_READONLY_DBMOVED => Self::ReadOnlyDbMoved,
libsqlite3_sys::SQLITE_READONLY_CANTINIT => Self::ReadOnlyCantInit,
libsqlite3_sys::SQLITE_READONLY_DIRECTORY => Self::ReadOnlyDirectory,
libsqlite3_sys::SQLITE_ABORT_ROLLBACK => Self::AbortRollback,
libsqlite3_sys::SQLITE_CONSTRAINT_CHECK => Self::ConstraintCheck,
libsqlite3_sys::SQLITE_CONSTRAINT_COMMITHOOK => Self::ConstraintCommitHook,
libsqlite3_sys::SQLITE_CONSTRAINT_FOREIGNKEY => Self::ConstraintForeignKey,
libsqlite3_sys::SQLITE_CONSTRAINT_FUNCTION => Self::ConstraintFunction,
libsqlite3_sys::SQLITE_CONSTRAINT_NOTNULL => Self::ConstraintNotNull,
libsqlite3_sys::SQLITE_CONSTRAINT_PRIMARYKEY => Self::ConstraintPrimaryKey,
libsqlite3_sys::SQLITE_CONSTRAINT_TRIGGER => Self::ConstraintTrigger,
libsqlite3_sys::SQLITE_CONSTRAINT_UNIQUE => Self::ConstraintUnique,
libsqlite3_sys::SQLITE_CONSTRAINT_VTAB => Self::ConstraintVTab,
libsqlite3_sys::SQLITE_CONSTRAINT_ROWID => Self::ConstraintRowId,
libsqlite3_sys::SQLITE_CONSTRAINT_PINNED => Self::ConstraintPinned,
libsqlite3_sys::SQLITE_CONSTRAINT_DATATYPE => Self::ConstraintDataType,
libsqlite3_sys::SQLITE_NOTICE_RECOVER_WAL => Self::NoticeRecoverWal,
libsqlite3_sys::SQLITE_NOTICE_RECOVER_ROLLBACK => Self::NoticeRecoverRollback,
libsqlite3_sys::SQLITE_WARNING_AUTOINDEX => Self::WarningAutoIndex,
libsqlite3_sys::SQLITE_AUTH_USER => Self::AuthUser,
libsqlite3_sys::SQLITE_OK_LOAD_PERMANENTLY => Self::OkLoadPermanently,
libsqlite3_sys::SQLITE_OK_SYMLINK => Self::OkSymlink,
_ => Self::Unknown(code as u32),
}
}
pub(crate) fn is_busy(&self) -> bool {
matches!(
self,
Self::BusyRecovery | Self::BusySnapshot | Self::BusyTimeout
)
}
}
#[derive(Debug, Clone, thiserror::Error)]
#[error("(code: {:?}) {message}", .extended)]
pub struct SqliteError {
pub primary: PrimaryErrCode,
pub extended: ExtendedErrCode,
pub message: String,
}
impl SqliteError {
pub(crate) fn new(handle: *mut sqlite3) -> Self {
let code = ffi::extended_errcode(handle);
let message = unsafe {
let msg = ffi::errmsg(handle);
debug_assert!(!msg.is_null());
CStr::from_ptr(msg).to_string_lossy().into_owned()
};
Self {
extended: ExtendedErrCode::from_code(code),
primary: PrimaryErrCode::from_code(code),
message,
}
}
pub(crate) fn is_busy(&self) -> bool {
self.primary == PrimaryErrCode::Busy || self.extended.is_busy()
}
pub(crate) fn should_retry(&self) -> bool {
self.primary == PrimaryErrCode::Locked
|| self.extended == ExtendedErrCode::LockedSharedCache
|| self.is_busy()
}
}