diff options
Diffstat (limited to 'src/error.rs')
-rw-r--r-- | src/error.rs | 96 |
1 files changed, 90 insertions, 6 deletions
diff --git a/src/error.rs b/src/error.rs index 129f697..3c264d3 100644 --- a/src/error.rs +++ b/src/error.rs @@ -34,7 +34,7 @@ pub enum Error { /// Error converting a string to a C-compatible string because it contained /// an embedded nul. - NulError(::std::ffi::NulError), + NulError(std::ffi::NulError), /// Error when using SQL named parameters and passing a parameter name not /// present in the SQL. @@ -128,6 +128,19 @@ pub enum Error { #[cfg(feature = "blob")] #[cfg_attr(docsrs, doc(cfg(feature = "blob")))] BlobSizeError, + /// Error referencing a specific token in the input SQL + #[cfg(feature = "modern_sqlite")] // 3.38.0 + #[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))] + SqlInputError { + /// error code + error: ffi::Error, + /// error message + msg: String, + /// SQL input + sql: String, + /// byte offset of the start of invalid token + offset: c_int, + }, } impl PartialEq for Error { @@ -172,6 +185,21 @@ impl PartialEq for Error { } #[cfg(feature = "blob")] (Error::BlobSizeError, Error::BlobSizeError) => true, + #[cfg(feature = "modern_sqlite")] + ( + Error::SqlInputError { + error: e1, + msg: m1, + sql: s1, + offset: o1, + }, + Error::SqlInputError { + error: e2, + msg: m2, + sql: s2, + offset: o2, + }, + ) => e1 == e2 && m1 == m2 && s1 == s2 && o1 == o2, (..) => false, } } @@ -184,14 +212,14 @@ impl From<str::Utf8Error> for Error { } } -impl From<::std::ffi::NulError> for Error { +impl From<std::ffi::NulError> for Error { #[cold] - fn from(err: ::std::ffi::NulError) -> Error { + fn from(err: std::ffi::NulError) -> Error { Error::NulError(err) } } -const UNKNOWN_COLUMN: usize = std::usize::MAX; +const UNKNOWN_COLUMN: usize = usize::MAX; /// The conversion isn't precise, but it's convenient to have it /// to allow use of `get_raw(…).as_…()?` in callbacks that take `Error`. @@ -281,9 +309,15 @@ impl fmt::Display for Error { #[cfg(feature = "functions")] Error::GetAuxWrongType => write!(f, "get_aux called with wrong type"), Error::MultipleStatement => write!(f, "Multiple statements provided"), - #[cfg(feature = "blob")] Error::BlobSizeError => "Blob size is insufficient".fmt(f), + #[cfg(feature = "modern_sqlite")] + Error::SqlInputError { + ref msg, + offset, + ref sql, + .. + } => write!(f, "{} in {} at offset {}", msg, sql, offset), } } } @@ -331,14 +365,35 @@ impl error::Error for Error { #[cfg(feature = "blob")] Error::BlobSizeError => None, + #[cfg(feature = "modern_sqlite")] + Error::SqlInputError { ref error, .. } => Some(error), } } } +impl Error { + /// Returns the underlying SQLite error if this is [`Error::SqliteFailure`]. + #[inline] + pub fn sqlite_error(&self) -> Option<&ffi::Error> { + match self { + Self::SqliteFailure(error, _) => Some(error), + _ => None, + } + } + + /// Returns the underlying SQLite error code if this is + /// [`Error::SqliteFailure`]. + #[inline] + pub fn sqlite_error_code(&self) -> Option<ffi::ErrorCode> { + self.sqlite_error().map(|error| error.code) + } +} + // These are public but not re-exported by lib.rs, so only visible within crate. #[cold] pub fn error_from_sqlite_code(code: c_int, message: Option<String>) -> Error { + // TODO sqlite3_error_offset // 3.38.0, #1130 Error::SqliteFailure(ffi::Error::new(code), message) } @@ -352,9 +407,38 @@ pub unsafe fn error_from_handle(db: *mut ffi::sqlite3, code: c_int) -> Error { error_from_sqlite_code(code, message) } +#[cold] +#[cfg(not(all(feature = "modern_sqlite", not(feature = "bundled-sqlcipher"))))] // SQLite >= 3.38.0 +pub unsafe fn error_with_offset(db: *mut ffi::sqlite3, code: c_int, _sql: &str) -> Error { + error_from_handle(db, code) +} + +#[cold] +#[cfg(all(feature = "modern_sqlite", not(feature = "bundled-sqlcipher")))] // SQLite >= 3.38.0 +pub unsafe fn error_with_offset(db: *mut ffi::sqlite3, code: c_int, sql: &str) -> Error { + if db.is_null() { + error_from_sqlite_code(code, None) + } else { + let error = ffi::Error::new(code); + let msg = errmsg_to_string(ffi::sqlite3_errmsg(db)); + if ffi::ErrorCode::Unknown == error.code { + let offset = ffi::sqlite3_error_offset(db); + if offset >= 0 { + return Error::SqlInputError { + error, + msg, + sql: sql.to_owned(), + offset, + }; + } + } + Error::SqliteFailure(error, Some(msg)) + } +} + pub fn check(code: c_int) -> Result<()> { if code != crate::ffi::SQLITE_OK { - Err(crate::error::error_from_sqlite_code(code, None)) + Err(error_from_sqlite_code(code, None)) } else { Ok(()) } |