1 use std::error;
2 use std::fmt;
3 use std::os::raw::c_int;
4 
5 /// Error Codes
6 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
7 #[non_exhaustive]
8 pub enum ErrorCode {
9     /// Internal logic error in SQLite
10     InternalMalfunction,
11     /// Access permission denied
12     PermissionDenied,
13     /// Callback routine requested an abort
14     OperationAborted,
15     /// The database file is locked
16     DatabaseBusy,
17     /// A table in the database is locked
18     DatabaseLocked,
19     /// A malloc() failed
20     OutOfMemory,
21     /// Attempt to write a readonly database
22     ReadOnly,
23     /// Operation terminated by sqlite3_interrupt()
24     OperationInterrupted,
25     /// Some kind of disk I/O error occurred
26     SystemIoFailure,
27     /// The database disk image is malformed
28     DatabaseCorrupt,
29     /// Unknown opcode in sqlite3_file_control()
30     NotFound,
31     /// Insertion failed because database is full
32     DiskFull,
33     /// Unable to open the database file
34     CannotOpen,
35     /// Database lock protocol error
36     FileLockingProtocolFailed,
37     /// The database schema changed
38     SchemaChanged,
39     /// String or BLOB exceeds size limit
40     TooBig,
41     /// Abort due to constraint violation
42     ConstraintViolation,
43     /// Data type mismatch
44     TypeMismatch,
45     /// Library used incorrectly
46     ApiMisuse,
47     /// Uses OS features not supported on host
48     NoLargeFileSupport,
49     /// Authorization denied
50     AuthorizationForStatementDenied,
51     /// 2nd parameter to sqlite3_bind out of range
52     ParameterOutOfRange,
53     /// File opened that is not a database file
54     NotADatabase,
55     /// SQL error or missing database
56     Unknown,
57 }
58 
59 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
60 pub struct Error {
61     pub code: ErrorCode,
62     pub extended_code: c_int,
63 }
64 
65 impl Error {
66     #[must_use]
new(result_code: c_int) -> Error67     pub fn new(result_code: c_int) -> Error {
68         let code = match result_code & 0xff {
69             super::SQLITE_INTERNAL => ErrorCode::InternalMalfunction,
70             super::SQLITE_PERM => ErrorCode::PermissionDenied,
71             super::SQLITE_ABORT => ErrorCode::OperationAborted,
72             super::SQLITE_BUSY => ErrorCode::DatabaseBusy,
73             super::SQLITE_LOCKED => ErrorCode::DatabaseLocked,
74             super::SQLITE_NOMEM => ErrorCode::OutOfMemory,
75             super::SQLITE_READONLY => ErrorCode::ReadOnly,
76             super::SQLITE_INTERRUPT => ErrorCode::OperationInterrupted,
77             super::SQLITE_IOERR => ErrorCode::SystemIoFailure,
78             super::SQLITE_CORRUPT => ErrorCode::DatabaseCorrupt,
79             super::SQLITE_NOTFOUND => ErrorCode::NotFound,
80             super::SQLITE_FULL => ErrorCode::DiskFull,
81             super::SQLITE_CANTOPEN => ErrorCode::CannotOpen,
82             super::SQLITE_PROTOCOL => ErrorCode::FileLockingProtocolFailed,
83             super::SQLITE_SCHEMA => ErrorCode::SchemaChanged,
84             super::SQLITE_TOOBIG => ErrorCode::TooBig,
85             super::SQLITE_CONSTRAINT => ErrorCode::ConstraintViolation,
86             super::SQLITE_MISMATCH => ErrorCode::TypeMismatch,
87             super::SQLITE_MISUSE => ErrorCode::ApiMisuse,
88             super::SQLITE_NOLFS => ErrorCode::NoLargeFileSupport,
89             super::SQLITE_AUTH => ErrorCode::AuthorizationForStatementDenied,
90             super::SQLITE_RANGE => ErrorCode::ParameterOutOfRange,
91             super::SQLITE_NOTADB => ErrorCode::NotADatabase,
92             _ => ErrorCode::Unknown,
93         };
94 
95         Error {
96             code,
97             extended_code: result_code,
98         }
99     }
100 }
101 
102 impl fmt::Display for Error {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result103     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104         write!(
105             f,
106             "Error code {}: {}",
107             self.extended_code,
108             code_to_str(self.extended_code)
109         )
110     }
111 }
112 
113 impl error::Error for Error {
description(&self) -> &str114     fn description(&self) -> &str {
115         code_to_str(self.extended_code)
116     }
117 }
118 
119 // Result codes.
120 // Note: These are not public because our bindgen bindings export whichever
121 // constants are present in the current version of SQLite. We repeat them here,
122 // so we don't have to worry about which version of SQLite added which
123 // constants, and we only use them to implement code_to_str below.
124 
125 // Extended result codes.
126 
127 const SQLITE_ERROR_MISSING_COLLSEQ: c_int = super::SQLITE_ERROR | (1 << 8);
128 const SQLITE_ERROR_RETRY: c_int = super::SQLITE_ERROR | (2 << 8);
129 const SQLITE_ERROR_SNAPSHOT: c_int = super::SQLITE_ERROR | (3 << 8);
130 
131 const SQLITE_IOERR_BEGIN_ATOMIC: c_int = super::SQLITE_IOERR | (29 << 8);
132 const SQLITE_IOERR_COMMIT_ATOMIC: c_int = super::SQLITE_IOERR | (30 << 8);
133 const SQLITE_IOERR_ROLLBACK_ATOMIC: c_int = super::SQLITE_IOERR | (31 << 8);
134 const SQLITE_IOERR_DATA: c_int = super::SQLITE_IOERR | (32 << 8);
135 const SQLITE_IOERR_CORRUPTFS: c_int = super::SQLITE_IOERR | (33 << 8);
136 const SQLITE_IOERR_IN_PAGE: c_int = super::SQLITE_IOERR | (34 << 8);
137 
138 const SQLITE_LOCKED_VTAB: c_int = super::SQLITE_LOCKED | (2 << 8);
139 
140 const SQLITE_BUSY_TIMEOUT: c_int = super::SQLITE_BUSY | (3 << 8);
141 
142 const SQLITE_CANTOPEN_SYMLINK: c_int = super::SQLITE_CANTOPEN | (6 << 8);
143 
144 const SQLITE_CORRUPT_SEQUENCE: c_int = super::SQLITE_CORRUPT | (2 << 8);
145 const SQLITE_CORRUPT_INDEX: c_int = super::SQLITE_CORRUPT | (3 << 8);
146 
147 const SQLITE_READONLY_CANTINIT: c_int = super::SQLITE_READONLY | (5 << 8);
148 const SQLITE_READONLY_DIRECTORY: c_int = super::SQLITE_READONLY | (6 << 8);
149 
150 const SQLITE_CONSTRAINT_PINNED: c_int = super::SQLITE_CONSTRAINT | (11 << 8);
151 const SQLITE_CONSTRAINT_DATATYPE: c_int = super::SQLITE_CONSTRAINT | (12 << 8);
152 
153 #[must_use]
code_to_str(code: c_int) -> &'static str154 pub fn code_to_str(code: c_int) -> &'static str {
155     match code {
156         super::SQLITE_OK        => "Successful result",
157         super::SQLITE_ERROR     => "SQL error or missing database",
158         super::SQLITE_INTERNAL  => "Internal logic error in SQLite",
159         super::SQLITE_PERM      => "Access permission denied",
160         super::SQLITE_ABORT     => "Callback routine requested an abort",
161         super::SQLITE_BUSY      => "The database file is locked",
162         super::SQLITE_LOCKED    => "A table in the database is locked",
163         super::SQLITE_NOMEM     => "A malloc() failed",
164         super::SQLITE_READONLY  => "Attempt to write a readonly database",
165         super::SQLITE_INTERRUPT => "Operation terminated by sqlite3_interrupt()",
166         super::SQLITE_IOERR     => "Some kind of disk I/O error occurred",
167         super::SQLITE_CORRUPT   => "The database disk image is malformed",
168         super::SQLITE_NOTFOUND  => "Unknown opcode in sqlite3_file_control()",
169         super::SQLITE_FULL      => "Insertion failed because database is full",
170         super::SQLITE_CANTOPEN  => "Unable to open the database file",
171         super::SQLITE_PROTOCOL  => "Database lock protocol error",
172         super::SQLITE_EMPTY     => "Database is empty",
173         super::SQLITE_SCHEMA    => "The database schema changed",
174         super::SQLITE_TOOBIG    => "String or BLOB exceeds size limit",
175         super::SQLITE_CONSTRAINT=> "Abort due to constraint violation",
176         super::SQLITE_MISMATCH  => "Data type mismatch",
177         super::SQLITE_MISUSE    => "Library used incorrectly",
178         super::SQLITE_NOLFS     => "Uses OS features not supported on host",
179         super::SQLITE_AUTH      => "Authorization denied",
180         super::SQLITE_FORMAT    => "Auxiliary database format error",
181         super::SQLITE_RANGE     => "2nd parameter to sqlite3_bind out of range",
182         super::SQLITE_NOTADB    => "File opened that is not a database file",
183         super::SQLITE_NOTICE    => "Notifications from sqlite3_log()",
184         super::SQLITE_WARNING   => "Warnings from sqlite3_log()",
185         super::SQLITE_ROW       => "sqlite3_step() has another row ready",
186         super::SQLITE_DONE      => "sqlite3_step() has finished executing",
187 
188         SQLITE_ERROR_MISSING_COLLSEQ   => "SQLITE_ERROR_MISSING_COLLSEQ",
189         SQLITE_ERROR_RETRY   => "SQLITE_ERROR_RETRY",
190         SQLITE_ERROR_SNAPSHOT   => "SQLITE_ERROR_SNAPSHOT",
191 
192         super::SQLITE_IOERR_READ              => "Error reading from disk",
193         super::SQLITE_IOERR_SHORT_READ        => "Unable to obtain number of requested bytes (file truncated?)",
194         super::SQLITE_IOERR_WRITE             => "Error writing to disk",
195         super::SQLITE_IOERR_FSYNC             => "Error flushing data to persistent storage (fsync)",
196         super::SQLITE_IOERR_DIR_FSYNC         => "Error calling fsync on a directory",
197         super::SQLITE_IOERR_TRUNCATE          => "Error attempting to truncate file",
198         super::SQLITE_IOERR_FSTAT             => "Error invoking fstat to get file metadata",
199         super::SQLITE_IOERR_UNLOCK            => "I/O error within xUnlock of a VFS object",
200         super::SQLITE_IOERR_RDLOCK            => "I/O error within xLock of a VFS object (trying to obtain a read lock)",
201         super::SQLITE_IOERR_DELETE            => "I/O error within xDelete of a VFS object",
202         super::SQLITE_IOERR_BLOCKED           => "SQLITE_IOERR_BLOCKED", // no longer used
203         super::SQLITE_IOERR_NOMEM             => "Out of memory in I/O layer",
204         super::SQLITE_IOERR_ACCESS            => "I/O error within xAccess of a VFS object",
205         super::SQLITE_IOERR_CHECKRESERVEDLOCK => "I/O error within then xCheckReservedLock method",
206         super::SQLITE_IOERR_LOCK              => "I/O error in the advisory file locking layer",
207         super::SQLITE_IOERR_CLOSE             => "I/O error within the xClose method",
208         super::SQLITE_IOERR_DIR_CLOSE         => "SQLITE_IOERR_DIR_CLOSE", // no longer used
209         super::SQLITE_IOERR_SHMOPEN           => "I/O error within the xShmMap method (trying to open a new shared-memory segment)",
210         super::SQLITE_IOERR_SHMSIZE           => "I/O error within the xShmMap method (trying to resize an existing shared-memory segment)",
211         super::SQLITE_IOERR_SHMLOCK           => "SQLITE_IOERR_SHMLOCK", // no longer used
212         super::SQLITE_IOERR_SHMMAP            => "I/O error within the xShmMap method (trying to map a shared-memory segment into process address space)",
213         super::SQLITE_IOERR_SEEK              => "I/O error within the xRead or xWrite (trying to seek within a file)",
214         super::SQLITE_IOERR_DELETE_NOENT      => "File being deleted does not exist",
215         super::SQLITE_IOERR_MMAP              => "I/O error while trying to map or unmap part of the database file into process address space",
216         super::SQLITE_IOERR_GETTEMPPATH       => "VFS is unable to determine a suitable directory for temporary files",
217         super::SQLITE_IOERR_CONVPATH          => "cygwin_conv_path() system call failed",
218         super::SQLITE_IOERR_VNODE             => "SQLITE_IOERR_VNODE", // not documented?
219         super::SQLITE_IOERR_AUTH              => "SQLITE_IOERR_AUTH",
220         SQLITE_IOERR_BEGIN_ATOMIC      => "SQLITE_IOERR_BEGIN_ATOMIC",
221         SQLITE_IOERR_COMMIT_ATOMIC     => "SQLITE_IOERR_COMMIT_ATOMIC",
222         SQLITE_IOERR_ROLLBACK_ATOMIC   => "SQLITE_IOERR_ROLLBACK_ATOMIC",
223         SQLITE_IOERR_DATA   => "SQLITE_IOERR_DATA",
224         SQLITE_IOERR_CORRUPTFS   => "SQLITE_IOERR_CORRUPTFS",
225         SQLITE_IOERR_IN_PAGE   => "SQLITE_IOERR_IN_PAGE",
226 
227         super::SQLITE_LOCKED_SHAREDCACHE      => "Locking conflict due to another connection with a shared cache",
228         SQLITE_LOCKED_VTAB             => "SQLITE_LOCKED_VTAB",
229 
230         super::SQLITE_BUSY_RECOVERY           => "Another process is recovering a WAL mode database file",
231         super::SQLITE_BUSY_SNAPSHOT           => "Cannot promote read transaction to write transaction because of writes by another connection",
232         SQLITE_BUSY_TIMEOUT           => "SQLITE_BUSY_TIMEOUT",
233 
234         super::SQLITE_CANTOPEN_NOTEMPDIR      => "SQLITE_CANTOPEN_NOTEMPDIR", // no longer used
235         super::SQLITE_CANTOPEN_ISDIR          => "Attempted to open directory as file",
236         super::SQLITE_CANTOPEN_FULLPATH       => "Unable to convert filename into full pathname",
237         super::SQLITE_CANTOPEN_CONVPATH       => "cygwin_conv_path() system call failed",
238         SQLITE_CANTOPEN_SYMLINK       => "SQLITE_CANTOPEN_SYMLINK",
239 
240         super::SQLITE_CORRUPT_VTAB            => "Content in the virtual table is corrupt",
241         SQLITE_CORRUPT_SEQUENCE        => "SQLITE_CORRUPT_SEQUENCE",
242         SQLITE_CORRUPT_INDEX        => "SQLITE_CORRUPT_INDEX",
243 
244         super::SQLITE_READONLY_RECOVERY       => "WAL mode database file needs recovery (requires write access)",
245         super::SQLITE_READONLY_CANTLOCK       => "Shared-memory file associated with WAL mode database is read-only",
246         super::SQLITE_READONLY_ROLLBACK       => "Database has hot journal that must be rolled back (requires write access)",
247         super::SQLITE_READONLY_DBMOVED        => "Database cannot be modified because database file has moved",
248         SQLITE_READONLY_CANTINIT       => "SQLITE_READONLY_CANTINIT",
249         SQLITE_READONLY_DIRECTORY      => "SQLITE_READONLY_DIRECTORY",
250 
251         super::SQLITE_ABORT_ROLLBACK          => "Transaction was rolled back",
252 
253         super::SQLITE_CONSTRAINT_CHECK        => "A CHECK constraint failed",
254         super::SQLITE_CONSTRAINT_COMMITHOOK   => "Commit hook caused rollback",
255         super::SQLITE_CONSTRAINT_FOREIGNKEY   => "Foreign key constraint failed",
256         super::SQLITE_CONSTRAINT_FUNCTION     => "Error returned from extension function",
257         super::SQLITE_CONSTRAINT_NOTNULL      => "A NOT NULL constraint failed",
258         super::SQLITE_CONSTRAINT_PRIMARYKEY   => "A PRIMARY KEY constraint failed",
259         super::SQLITE_CONSTRAINT_TRIGGER      => "A RAISE function within a trigger fired",
260         super::SQLITE_CONSTRAINT_UNIQUE       => "A UNIQUE constraint failed",
261         super::SQLITE_CONSTRAINT_VTAB         => "An application-defined virtual table error occurred",
262         super::SQLITE_CONSTRAINT_ROWID        => "A non-unique rowid occurred",
263         SQLITE_CONSTRAINT_PINNED        => "SQLITE_CONSTRAINT_PINNED",
264         SQLITE_CONSTRAINT_DATATYPE        => "SQLITE_CONSTRAINT_DATATYPE",
265 
266         super::SQLITE_NOTICE_RECOVER_WAL      => "A WAL mode database file was recovered",
267         super::SQLITE_NOTICE_RECOVER_ROLLBACK => "Hot journal was rolled back",
268 
269         super::SQLITE_WARNING_AUTOINDEX       => "Automatic indexing used - database might benefit from additional indexes",
270 
271         super::SQLITE_AUTH_USER               => "SQLITE_AUTH_USER", // not documented?
272 
273         _ => "Unknown error code",
274     }
275 }
276 
277 /// Loadable extension initialization error
278 #[cfg(feature = "loadable_extension")]
279 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
280 #[non_exhaustive]
281 pub enum InitError {
282     /// Version mismatch between the extension and the SQLite3 library
283     VersionMismatch { compile_time: i32, runtime: i32 },
284     /// Invalid function pointer in one of sqlite3_api_routines fields
285     NullFunctionPointer,
286 }
287 #[cfg(feature = "loadable_extension")]
288 impl ::std::fmt::Display for InitError {
fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result289     fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
290         match *self {
291             InitError::VersionMismatch {
292                 compile_time,
293                 runtime,
294             } => {
295                 write!(f, "SQLite version mismatch: {runtime} < {compile_time}")
296             }
297             InitError::NullFunctionPointer => {
298                 write!(f, "Some sqlite3_api_routines fields are null")
299             }
300         }
301     }
302 }
303 #[cfg(feature = "loadable_extension")]
304 impl error::Error for InitError {}
305 
306 #[cfg(test)]
307 mod test {
308     use crate::*;
309 
310     #[test]
error_new()311     pub fn error_new() {
312         let assoc = vec![
313             (SQLITE_INTERNAL, ErrorCode::InternalMalfunction),
314             (SQLITE_PERM, ErrorCode::PermissionDenied),
315             (SQLITE_ABORT_ROLLBACK, ErrorCode::OperationAborted),
316             (SQLITE_BUSY_RECOVERY, ErrorCode::DatabaseBusy),
317             (SQLITE_LOCKED_SHAREDCACHE, ErrorCode::DatabaseLocked),
318             (SQLITE_NOMEM, ErrorCode::OutOfMemory),
319             (SQLITE_IOERR_READ, ErrorCode::SystemIoFailure),
320             (SQLITE_NOTFOUND, ErrorCode::NotFound),
321             (SQLITE_FULL, ErrorCode::DiskFull),
322             (SQLITE_PROTOCOL, ErrorCode::FileLockingProtocolFailed),
323             (SQLITE_SCHEMA, ErrorCode::SchemaChanged),
324             (SQLITE_TOOBIG, ErrorCode::TooBig),
325             (SQLITE_MISMATCH, ErrorCode::TypeMismatch),
326             (SQLITE_NOLFS, ErrorCode::NoLargeFileSupport),
327             (SQLITE_RANGE, ErrorCode::ParameterOutOfRange),
328             (SQLITE_NOTADB, ErrorCode::NotADatabase),
329         ];
330         for (sqlite_code, rust_code) in assoc {
331             let err = Error::new(sqlite_code);
332             assert_eq!(
333                 err,
334                 Error {
335                     code: rust_code,
336                     extended_code: sqlite_code
337                 }
338             );
339             let s = format!("{}", err);
340             assert!(!s.is_empty());
341         }
342     }
343 }
344