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