aboutsummaryrefslogtreecommitdiff
path: root/src/error.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/error.rs')
-rw-r--r--src/error.rs96
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(())
}