1use std::error;
2use std::fmt;
3use std::os::raw::c_int;
4
5#[derive(Clone, Copy, Debug, PartialEq, Eq)]
7#[non_exhaustive]
8pub enum ErrorCode {
9 InternalMalfunction,
11 PermissionDenied,
13 OperationAborted,
15 DatabaseBusy,
17 DatabaseLocked,
19 OutOfMemory,
21 ReadOnly,
23 OperationInterrupted,
25 SystemIoFailure,
27 DatabaseCorrupt,
29 NotFound,
31 DiskFull,
33 CannotOpen,
35 FileLockingProtocolFailed,
37 SchemaChanged,
39 TooBig,
41 ConstraintViolation,
43 TypeMismatch,
45 ApiMisuse,
47 NoLargeFileSupport,
49 AuthorizationForStatementDenied,
51 ParameterOutOfRange,
53 NotADatabase,
55 Unknown,
57}
58
59#[derive(Clone, Copy, Debug, PartialEq, Eq)]
60pub struct Error {
61 pub code: ErrorCode,
62 pub extended_code: c_int,
63}
64
65impl Error {
66 #[must_use]
67 pub fn new(result_code: c_int) -> Self {
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 Self {
96 code,
97 extended_code: result_code,
98 }
99 }
100}
101
102impl fmt::Display for Error {
103 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
113impl error::Error for Error {
114 fn description(&self) -> &str {
115 code_to_str(self.extended_code)
116 }
117}
118
119const SQLITE_ERROR_MISSING_COLLSEQ: c_int = super::SQLITE_ERROR | (1 << 8);
128const SQLITE_ERROR_RETRY: c_int = super::SQLITE_ERROR | (2 << 8);
129const SQLITE_ERROR_SNAPSHOT: c_int = super::SQLITE_ERROR | (3 << 8);
130
131const SQLITE_IOERR_BEGIN_ATOMIC: c_int = super::SQLITE_IOERR | (29 << 8);
132const SQLITE_IOERR_COMMIT_ATOMIC: c_int = super::SQLITE_IOERR | (30 << 8);
133const SQLITE_IOERR_ROLLBACK_ATOMIC: c_int = super::SQLITE_IOERR | (31 << 8);
134const SQLITE_IOERR_DATA: c_int = super::SQLITE_IOERR | (32 << 8);
135const SQLITE_IOERR_CORRUPTFS: c_int = super::SQLITE_IOERR | (33 << 8);
136const SQLITE_IOERR_IN_PAGE: c_int = super::SQLITE_IOERR | (34 << 8);
137
138const SQLITE_LOCKED_VTAB: c_int = super::SQLITE_LOCKED | (2 << 8);
139
140const SQLITE_BUSY_TIMEOUT: c_int = super::SQLITE_BUSY | (3 << 8);
141
142const SQLITE_CANTOPEN_SYMLINK: c_int = super::SQLITE_CANTOPEN | (6 << 8);
143
144const SQLITE_CORRUPT_SEQUENCE: c_int = super::SQLITE_CORRUPT | (2 << 8);
145const SQLITE_CORRUPT_INDEX: c_int = super::SQLITE_CORRUPT | (3 << 8);
146
147const SQLITE_READONLY_CANTINIT: c_int = super::SQLITE_READONLY | (5 << 8);
148const SQLITE_READONLY_DIRECTORY: c_int = super::SQLITE_READONLY | (6 << 8);
149
150const SQLITE_CONSTRAINT_PINNED: c_int = super::SQLITE_CONSTRAINT | (11 << 8);
151const SQLITE_CONSTRAINT_DATATYPE: c_int = super::SQLITE_CONSTRAINT | (12 << 8);
152
153#[must_use]
154pub 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", 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", 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", 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", 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", 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", _ => "Unknown error code",
274 }
275}
276
277#[cfg(feature = "loadable_extension")]
279#[derive(Clone, Copy, Debug, PartialEq, Eq)]
280#[non_exhaustive]
281pub enum InitError {
282 VersionMismatch { compile_time: i32, runtime: i32 },
284 NullFunctionPointer,
286}
287#[cfg(feature = "loadable_extension")]
288impl fmt::Display for InitError {
289 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
290 match *self {
291 Self::VersionMismatch {
292 compile_time,
293 runtime,
294 } => {
295 write!(f, "SQLite version mismatch: {runtime} < {compile_time}")
296 }
297 Self::NullFunctionPointer => {
298 write!(f, "Some sqlite3_api_routines fields are null")
299 }
300 }
301 }
302}
303#[cfg(feature = "loadable_extension")]
304impl error::Error for InitError {}
305
306#[cfg(test)]
307mod test {
308 use crate::*;
309
310 #[test]
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}