libsqlite3_sys/
error.rs

1use core::error;
2use core::ffi::c_int;
3use core::ffi::CStr;
4use core::fmt;
5
6/// Error Codes
7#[derive(Clone, Copy, Debug, PartialEq, Eq)]
8#[non_exhaustive]
9pub enum ErrorCode {
10    /// Internal logic error in SQLite
11    InternalMalfunction,
12    /// Access permission denied
13    PermissionDenied,
14    /// Callback routine requested an abort
15    OperationAborted,
16    /// The database file is locked
17    DatabaseBusy,
18    /// A table in the database is locked
19    DatabaseLocked,
20    /// A `malloc()` failed
21    OutOfMemory,
22    /// Attempt to write a readonly database
23    ReadOnly,
24    /// Operation terminated by `sqlite3_interrupt()`
25    OperationInterrupted,
26    /// Some kind of disk I/O error occurred
27    SystemIoFailure,
28    /// The database disk image is malformed
29    DatabaseCorrupt,
30    /// Unknown opcode in `sqlite3_file_control()`
31    NotFound,
32    /// Insertion failed because database is full
33    DiskFull,
34    /// Unable to open the database file
35    CannotOpen,
36    /// Database lock protocol error
37    FileLockingProtocolFailed,
38    /// The database schema changed
39    SchemaChanged,
40    /// String or BLOB exceeds size limit
41    TooBig,
42    /// Abort due to constraint violation
43    ConstraintViolation,
44    /// Data type mismatch
45    TypeMismatch,
46    /// Library used incorrectly
47    ApiMisuse,
48    /// Uses OS features not supported on host
49    NoLargeFileSupport,
50    /// Authorization denied
51    AuthorizationForStatementDenied,
52    /// 2nd parameter to `sqlite3_bind` out of range
53    ParameterOutOfRange,
54    /// File opened that is not a database file
55    NotADatabase,
56    /// SQL error or missing database
57    Unknown,
58}
59
60#[derive(Clone, Copy, Debug, PartialEq, Eq)]
61pub struct Error {
62    pub code: ErrorCode,
63    pub extended_code: c_int,
64}
65
66impl Error {
67    #[must_use]
68    pub fn new(result_code: c_int) -> Self {
69        let code = match result_code & 0xff {
70            super::SQLITE_INTERNAL => ErrorCode::InternalMalfunction,
71            super::SQLITE_PERM => ErrorCode::PermissionDenied,
72            super::SQLITE_ABORT => ErrorCode::OperationAborted,
73            super::SQLITE_BUSY => ErrorCode::DatabaseBusy,
74            super::SQLITE_LOCKED => ErrorCode::DatabaseLocked,
75            super::SQLITE_NOMEM => ErrorCode::OutOfMemory,
76            super::SQLITE_READONLY => ErrorCode::ReadOnly,
77            super::SQLITE_INTERRUPT => ErrorCode::OperationInterrupted,
78            super::SQLITE_IOERR => ErrorCode::SystemIoFailure,
79            super::SQLITE_CORRUPT => ErrorCode::DatabaseCorrupt,
80            super::SQLITE_NOTFOUND => ErrorCode::NotFound,
81            super::SQLITE_FULL => ErrorCode::DiskFull,
82            super::SQLITE_CANTOPEN => ErrorCode::CannotOpen,
83            super::SQLITE_PROTOCOL => ErrorCode::FileLockingProtocolFailed,
84            super::SQLITE_SCHEMA => ErrorCode::SchemaChanged,
85            super::SQLITE_TOOBIG => ErrorCode::TooBig,
86            super::SQLITE_CONSTRAINT => ErrorCode::ConstraintViolation,
87            super::SQLITE_MISMATCH => ErrorCode::TypeMismatch,
88            super::SQLITE_MISUSE => ErrorCode::ApiMisuse,
89            super::SQLITE_NOLFS => ErrorCode::NoLargeFileSupport,
90            super::SQLITE_AUTH => ErrorCode::AuthorizationForStatementDenied,
91            super::SQLITE_RANGE => ErrorCode::ParameterOutOfRange,
92            super::SQLITE_NOTADB => ErrorCode::NotADatabase,
93            _ => ErrorCode::Unknown,
94        };
95
96        Self {
97            code,
98            extended_code: result_code,
99        }
100    }
101}
102
103impl fmt::Display for Error {
104    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105        write!(
106            f,
107            "Error code {}: {}",
108            self.extended_code,
109            code_to_str(self.extended_code)
110        )
111    }
112}
113
114impl error::Error for Error {
115    fn description(&self) -> &str {
116        code_to_str(self.extended_code)
117    }
118}
119
120#[must_use]
121pub fn code_to_str(code: c_int) -> &'static str {
122    let err_str = unsafe { super::sqlite3_errstr(code) };
123    if err_str.is_null() {
124        "Unknown errod code"
125    } else {
126        // We know these values to be plain ASCII
127        unsafe { CStr::from_ptr(err_str) }.to_str().unwrap()
128    }
129}
130
131/// Loadable extension initialization error
132#[cfg(feature = "loadable_extension")]
133#[derive(Clone, Copy, Debug, PartialEq, Eq)]
134#[non_exhaustive]
135pub enum InitError {
136    /// Version mismatch between the extension and the SQLite3 library
137    VersionMismatch { compile_time: i32, runtime: i32 },
138    /// Invalid function pointer in one of `sqlite3_api_routines` fields
139    NullFunctionPointer,
140}
141#[cfg(feature = "loadable_extension")]
142impl fmt::Display for InitError {
143    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
144        match *self {
145            Self::VersionMismatch {
146                compile_time,
147                runtime,
148            } => {
149                write!(f, "SQLite version mismatch: {runtime} < {compile_time}")
150            }
151            Self::NullFunctionPointer => {
152                write!(f, "Some sqlite3_api_routines fields are null")
153            }
154        }
155    }
156}
157#[cfg(feature = "loadable_extension")]
158impl error::Error for InitError {}
159
160#[cfg(test)]
161mod test {
162    use crate::*;
163
164    #[test]
165    pub fn error_new() {
166        let assoc = vec![
167            (SQLITE_INTERNAL, ErrorCode::InternalMalfunction),
168            (SQLITE_PERM, ErrorCode::PermissionDenied),
169            (SQLITE_ABORT_ROLLBACK, ErrorCode::OperationAborted),
170            (SQLITE_BUSY_RECOVERY, ErrorCode::DatabaseBusy),
171            (SQLITE_LOCKED_SHAREDCACHE, ErrorCode::DatabaseLocked),
172            (SQLITE_NOMEM, ErrorCode::OutOfMemory),
173            (SQLITE_IOERR_READ, ErrorCode::SystemIoFailure),
174            (SQLITE_NOTFOUND, ErrorCode::NotFound),
175            (SQLITE_FULL, ErrorCode::DiskFull),
176            (SQLITE_PROTOCOL, ErrorCode::FileLockingProtocolFailed),
177            (SQLITE_SCHEMA, ErrorCode::SchemaChanged),
178            (SQLITE_TOOBIG, ErrorCode::TooBig),
179            (SQLITE_MISMATCH, ErrorCode::TypeMismatch),
180            (SQLITE_NOLFS, ErrorCode::NoLargeFileSupport),
181            (SQLITE_RANGE, ErrorCode::ParameterOutOfRange),
182            (SQLITE_NOTADB, ErrorCode::NotADatabase),
183        ];
184        for (sqlite_code, rust_code) in assoc {
185            let err = Error::new(sqlite_code);
186            assert_eq!(
187                err,
188                Error {
189                    code: rust_code,
190                    extended_code: sqlite_code
191                }
192            );
193            let s = format!("{err}");
194            assert!(!s.is_empty());
195        }
196    }
197}