Introduce SqlInputError with offset

This commit is contained in:
gwenn 2022-03-13 17:31:07 +01:00
parent c3b419b1e5
commit 69a40526d5
3 changed files with 75 additions and 3 deletions

View File

@ -128,6 +128,17 @@ 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,
/// byte offset of the start of invalid token
offset: c_int,
},
}
impl PartialEq for Error {
@ -172,6 +183,19 @@ impl PartialEq for Error {
}
#[cfg(feature = "blob")]
(Error::BlobSizeError, Error::BlobSizeError) => true,
#[cfg(feature = "modern_sqlite")]
(
Error::SqlInputError {
error: e1,
msg: s1,
offset: o1,
},
Error::SqlInputError {
error: e2,
msg: s2,
offset: o2,
},
) => e1 == e2 && s1 == s2 && o1 == o2,
(..) => false,
}
}
@ -281,9 +305,12 @@ 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, ..
} => write!(f, "{} at offset {}", msg, offset),
}
}
}
@ -331,6 +358,8 @@ impl error::Error for Error {
#[cfg(feature = "blob")]
Error::BlobSizeError => None,
#[cfg(feature = "modern_sqlite")]
Error::SqlInputError { ref error, .. } => Some(error),
}
}
}
@ -352,6 +381,30 @@ pub unsafe fn error_from_handle(db: *mut ffi::sqlite3, code: c_int) -> Error {
error_from_sqlite_code(code, message)
}
#[cold]
#[cfg(not(feature = "modern_sqlite"))]
pub unsafe fn error_with_offset(db: *mut ffi::sqlite3, code: c_int) -> Error {
error_from_handle(db, code)
}
#[cold]
#[cfg(feature = "modern_sqlite")] // 3.38.0
pub unsafe fn error_with_offset(db: *mut ffi::sqlite3, code: c_int) -> 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, 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))

View File

@ -10,7 +10,7 @@ use std::sync::{Arc, Mutex};
use super::ffi;
use super::str_for_sqlite;
use super::{Connection, InterruptHandle, OpenFlags, Result};
use crate::error::{error_from_handle, error_from_sqlite_code, Error};
use crate::error::{error_from_handle, error_from_sqlite_code, error_with_offset, Error};
use crate::raw_statement::RawStatement;
use crate::statement::Statement;
use crate::version::version_number;
@ -255,7 +255,9 @@ impl InnerConnection {
rc
};
// If there is an error, *ppStmt is set to NULL.
self.decode_result(r)?;
if r != ffi::SQLITE_OK {
return Err(unsafe { error_with_offset(self.db, r) });
}
// If the input text contains no SQL (if the input is an empty string or a
// comment) then *ppStmt is set to NULL.
let c_stmt: *mut ffi::sqlite3_stmt = c_stmt;

View File

@ -1508,4 +1508,21 @@ mod test {
assert_eq!(expected, actual);
Ok(())
}
#[test]
#[cfg(feature = "modern_sqlite")]
fn test_error_offset() -> Result<()> {
use crate::ffi::ErrorCode;
let db = Connection::open_in_memory()?;
let r = db.execute_batch("SELECT CURRENT_TIMESTANP;");
assert!(r.is_err());
match r.unwrap_err() {
Error::SqlInputError { error, offset, .. } => {
assert_eq!(error.code, ErrorCode::Unknown);
assert_eq!(offset, 7);
}
err => panic!("Unexpected error {}", err),
}
Ok(())
}
}