Skip to main content

rusqlite/
error.rs

1use crate::types::FromSqlError;
2use crate::types::Type;
3use crate::{errmsg_to_string, ffi, Result};
4use std::error;
5use std::fmt;
6use std::os::raw::c_int;
7use std::path::PathBuf;
8use std::str;
9
10/// Enum listing possible errors from rusqlite.
11#[derive(Debug)]
12#[allow(clippy::enum_variant_names)]
13#[non_exhaustive]
14pub enum Error {
15    /// An error from an underlying SQLite call.
16    SqliteFailure(ffi::Error, Option<String>),
17
18    /// Error reported when attempting to open a connection when SQLite was
19    /// configured to allow single-threaded use only.
20    SqliteSingleThreadedMode,
21
22    /// Error when the value of a particular column is requested, but it cannot
23    /// be converted to the requested Rust type.
24    FromSqlConversionFailure(usize, Type, Box<dyn error::Error + Send + Sync + 'static>),
25
26    /// Error when SQLite gives us an integral value outside the range of the
27    /// requested type (e.g., trying to get the value 1000 into a `u8`).
28    /// The associated `usize` is the column index,
29    /// and the associated `i64` is the value returned by SQLite.
30    IntegralValueOutOfRange(usize, i64),
31
32    /// Error converting a string to UTF-8.
33    Utf8Error(str::Utf8Error),
34
35    /// Error converting a string to a C-compatible string because it contained
36    /// an embedded nul.
37    NulError(std::ffi::NulError),
38
39    /// Error when using SQL named parameters and passing a parameter name not
40    /// present in the SQL.
41    InvalidParameterName(String),
42
43    /// Error converting a file path to a string.
44    InvalidPath(PathBuf),
45
46    /// Error returned when an [`execute`](crate::Connection::execute) call
47    /// returns rows.
48    ExecuteReturnedResults,
49
50    /// Error when a query that was expected to return at least one row (e.g.,
51    /// for [`query_row`](crate::Connection::query_row)) did not return any.
52    QueryReturnedNoRows,
53
54    /// Error when the value of a particular column is requested, but the index
55    /// is out of range for the statement.
56    InvalidColumnIndex(usize),
57
58    /// Error when the value of a named column is requested, but no column
59    /// matches the name for the statement.
60    InvalidColumnName(String),
61
62    /// Error when the value of a particular column is requested, but the type
63    /// of the result in that column cannot be converted to the requested
64    /// Rust type.
65    InvalidColumnType(usize, String, Type),
66
67    /// Error when a query that was expected to insert one row did not insert
68    /// any or insert many.
69    StatementChangedRows(usize),
70
71    /// Error returned by
72    /// [`functions::Context::get`](crate::functions::Context::get) when the
73    /// function argument cannot be converted to the requested type.
74    #[cfg(feature = "functions")]
75    #[cfg_attr(docsrs, doc(cfg(feature = "functions")))]
76    InvalidFunctionParameterType(usize, Type),
77    /// Error returned by [`vtab::Values::get`](crate::vtab::Values::get) when
78    /// the filter argument cannot be converted to the requested type.
79    #[cfg(feature = "vtab")]
80    #[cfg_attr(docsrs, doc(cfg(feature = "vtab")))]
81    InvalidFilterParameterType(usize, Type),
82
83    /// An error case available for implementors of custom user functions (e.g.,
84    /// [`create_scalar_function`](crate::Connection::create_scalar_function)).
85    #[cfg(feature = "functions")]
86    #[cfg_attr(docsrs, doc(cfg(feature = "functions")))]
87    #[allow(dead_code)]
88    UserFunctionError(Box<dyn error::Error + Send + Sync + 'static>),
89
90    /// Error available for the implementors of the
91    /// [`ToSql`](crate::types::ToSql) trait.
92    ToSqlConversionFailure(Box<dyn error::Error + Send + Sync + 'static>),
93
94    /// Error when the SQL is not a `SELECT`, is not read-only.
95    InvalidQuery,
96
97    /// An error case available for implementors of custom modules (e.g.,
98    /// [`create_module`](crate::Connection::create_module)).
99    #[cfg(feature = "vtab")]
100    #[cfg_attr(docsrs, doc(cfg(feature = "vtab")))]
101    #[allow(dead_code)]
102    ModuleError(String),
103
104    /// An unwinding panic occurs in a UDF (user-defined function).
105    UnwindingPanic,
106
107    /// An error returned when
108    /// [`Context::get_aux`](crate::functions::Context::get_aux) attempts to
109    /// retrieve data of a different type than what had been stored using
110    /// [`Context::set_aux`](crate::functions::Context::set_aux).
111    #[cfg(feature = "functions")]
112    #[cfg_attr(docsrs, doc(cfg(feature = "functions")))]
113    GetAuxWrongType,
114
115    /// Error when the SQL contains multiple statements.
116    MultipleStatement,
117    /// Error when the number of bound parameters does not match the number of
118    /// parameters in the query. The first `usize` is how many parameters were
119    /// given, the 2nd is how many were expected.
120    InvalidParameterCount(usize, usize),
121
122    /// Returned from various functions in the Blob IO positional API. For
123    /// example,
124    /// [`Blob::raw_read_at_exact`](crate::blob::Blob::raw_read_at_exact) will
125    /// return it if the blob has insufficient data.
126    #[cfg(feature = "blob")]
127    #[cfg_attr(docsrs, doc(cfg(feature = "blob")))]
128    BlobSizeError,
129    /// Error referencing a specific token in the input SQL
130    #[cfg(feature = "modern_sqlite")] // 3.38.0
131    #[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))]
132    SqlInputError {
133        /// error code
134        error: ffi::Error,
135        /// error message
136        msg: String,
137        /// SQL input
138        sql: String,
139        /// byte offset of the start of invalid token
140        offset: c_int,
141    },
142    /// Loadable extension initialization error
143    #[cfg(feature = "loadable_extension")]
144    #[cfg_attr(docsrs, doc(cfg(feature = "loadable_extension")))]
145    InitError(ffi::InitError),
146    /// Error when the schema of a particular database is requested, but the index
147    /// is out of range.
148    #[cfg(feature = "modern_sqlite")] // 3.39.0
149    #[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))]
150    InvalidDatabaseIndex(usize),
151}
152
153impl PartialEq for Error {
154    fn eq(&self, other: &Error) -> bool {
155        match (self, other) {
156            (Error::SqliteFailure(e1, s1), Error::SqliteFailure(e2, s2)) => e1 == e2 && s1 == s2,
157            (Error::SqliteSingleThreadedMode, Error::SqliteSingleThreadedMode) => true,
158            (Error::IntegralValueOutOfRange(i1, n1), Error::IntegralValueOutOfRange(i2, n2)) => {
159                i1 == i2 && n1 == n2
160            }
161            (Error::Utf8Error(e1), Error::Utf8Error(e2)) => e1 == e2,
162            (Error::NulError(e1), Error::NulError(e2)) => e1 == e2,
163            (Error::InvalidParameterName(n1), Error::InvalidParameterName(n2)) => n1 == n2,
164            (Error::InvalidPath(p1), Error::InvalidPath(p2)) => p1 == p2,
165            (Error::ExecuteReturnedResults, Error::ExecuteReturnedResults) => true,
166            (Error::QueryReturnedNoRows, Error::QueryReturnedNoRows) => true,
167            (Error::InvalidColumnIndex(i1), Error::InvalidColumnIndex(i2)) => i1 == i2,
168            (Error::InvalidColumnName(n1), Error::InvalidColumnName(n2)) => n1 == n2,
169            (Error::InvalidColumnType(i1, n1, t1), Error::InvalidColumnType(i2, n2, t2)) => {
170                i1 == i2 && t1 == t2 && n1 == n2
171            }
172            (Error::StatementChangedRows(n1), Error::StatementChangedRows(n2)) => n1 == n2,
173            #[cfg(feature = "functions")]
174            (
175                Error::InvalidFunctionParameterType(i1, t1),
176                Error::InvalidFunctionParameterType(i2, t2),
177            ) => i1 == i2 && t1 == t2,
178            #[cfg(feature = "vtab")]
179            (
180                Error::InvalidFilterParameterType(i1, t1),
181                Error::InvalidFilterParameterType(i2, t2),
182            ) => i1 == i2 && t1 == t2,
183            (Error::InvalidQuery, Error::InvalidQuery) => true,
184            #[cfg(feature = "vtab")]
185            (Error::ModuleError(s1), Error::ModuleError(s2)) => s1 == s2,
186            (Error::UnwindingPanic, Error::UnwindingPanic) => true,
187            #[cfg(feature = "functions")]
188            (Error::GetAuxWrongType, Error::GetAuxWrongType) => true,
189            (Error::InvalidParameterCount(i1, n1), Error::InvalidParameterCount(i2, n2)) => {
190                i1 == i2 && n1 == n2
191            }
192            #[cfg(feature = "blob")]
193            (Error::BlobSizeError, Error::BlobSizeError) => true,
194            #[cfg(feature = "modern_sqlite")]
195            (
196                Error::SqlInputError {
197                    error: e1,
198                    msg: m1,
199                    sql: s1,
200                    offset: o1,
201                },
202                Error::SqlInputError {
203                    error: e2,
204                    msg: m2,
205                    sql: s2,
206                    offset: o2,
207                },
208            ) => e1 == e2 && m1 == m2 && s1 == s2 && o1 == o2,
209            #[cfg(feature = "loadable_extension")]
210            (Error::InitError(e1), Error::InitError(e2)) => e1 == e2,
211            #[cfg(feature = "modern_sqlite")]
212            (Error::InvalidDatabaseIndex(i1), Error::InvalidDatabaseIndex(i2)) => i1 == i2,
213            (..) => false,
214        }
215    }
216}
217
218impl From<str::Utf8Error> for Error {
219    #[cold]
220    fn from(err: str::Utf8Error) -> Error {
221        Error::Utf8Error(err)
222    }
223}
224
225impl From<std::ffi::NulError> for Error {
226    #[cold]
227    fn from(err: std::ffi::NulError) -> Error {
228        Error::NulError(err)
229    }
230}
231
232const UNKNOWN_COLUMN: usize = usize::MAX;
233
234/// The conversion isn't precise, but it's convenient to have it
235/// to allow use of `get_raw(…).as_…()?` in callbacks that take `Error`.
236impl From<FromSqlError> for Error {
237    #[cold]
238    fn from(err: FromSqlError) -> Error {
239        // The error type requires index and type fields, but they aren't known in this
240        // context.
241        match err {
242            FromSqlError::OutOfRange(val) => Error::IntegralValueOutOfRange(UNKNOWN_COLUMN, val),
243            FromSqlError::InvalidBlobSize { .. } => {
244                Error::FromSqlConversionFailure(UNKNOWN_COLUMN, Type::Blob, Box::new(err))
245            }
246            FromSqlError::Other(source) => {
247                Error::FromSqlConversionFailure(UNKNOWN_COLUMN, Type::Null, source)
248            }
249            _ => Error::FromSqlConversionFailure(UNKNOWN_COLUMN, Type::Null, Box::new(err)),
250        }
251    }
252}
253
254#[cfg(feature = "loadable_extension")]
255impl From<ffi::InitError> for Error {
256    #[cold]
257    fn from(err: ffi::InitError) -> Error {
258        Error::InitError(err)
259    }
260}
261
262impl fmt::Display for Error {
263    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
264        match *self {
265            Error::SqliteFailure(ref err, None) => err.fmt(f),
266            Error::SqliteFailure(_, Some(ref s)) => write!(f, "{s}"),
267            Error::SqliteSingleThreadedMode => write!(
268                f,
269                "SQLite was compiled or configured for single-threaded use only"
270            ),
271            Error::FromSqlConversionFailure(i, ref t, ref err) => {
272                if i != UNKNOWN_COLUMN {
273                    write!(f, "Conversion error from type {t} at index: {i}, {err}")
274                } else {
275                    err.fmt(f)
276                }
277            }
278            Error::IntegralValueOutOfRange(col, val) => {
279                if col != UNKNOWN_COLUMN {
280                    write!(f, "Integer {val} out of range at index {col}")
281                } else {
282                    write!(f, "Integer {val} out of range")
283                }
284            }
285            Error::Utf8Error(ref err) => err.fmt(f),
286            Error::NulError(ref err) => err.fmt(f),
287            Error::InvalidParameterName(ref name) => write!(f, "Invalid parameter name: {name}"),
288            Error::InvalidPath(ref p) => write!(f, "Invalid path: {}", p.to_string_lossy()),
289            Error::ExecuteReturnedResults => {
290                write!(f, "Execute returned results - did you mean to call query?")
291            }
292            Error::QueryReturnedNoRows => write!(f, "Query returned no rows"),
293            Error::InvalidColumnIndex(i) => write!(f, "Invalid column index: {i}"),
294            Error::InvalidColumnName(ref name) => write!(f, "Invalid column name: {name}"),
295            Error::InvalidColumnType(i, ref name, ref t) => {
296                write!(f, "Invalid column type {t} at index: {i}, name: {name}")
297            }
298            Error::InvalidParameterCount(i1, n1) => write!(
299                f,
300                "Wrong number of parameters passed to query. Got {i1}, needed {n1}"
301            ),
302            Error::StatementChangedRows(i) => write!(f, "Query changed {i} rows"),
303
304            #[cfg(feature = "functions")]
305            Error::InvalidFunctionParameterType(i, ref t) => {
306                write!(f, "Invalid function parameter type {t} at index {i}")
307            }
308            #[cfg(feature = "vtab")]
309            Error::InvalidFilterParameterType(i, ref t) => {
310                write!(f, "Invalid filter parameter type {t} at index {i}")
311            }
312            #[cfg(feature = "functions")]
313            Error::UserFunctionError(ref err) => err.fmt(f),
314            Error::ToSqlConversionFailure(ref err) => err.fmt(f),
315            Error::InvalidQuery => write!(f, "Query is not read-only"),
316            #[cfg(feature = "vtab")]
317            Error::ModuleError(ref desc) => write!(f, "{desc}"),
318            Error::UnwindingPanic => write!(f, "unwinding panic"),
319            #[cfg(feature = "functions")]
320            Error::GetAuxWrongType => write!(f, "get_aux called with wrong type"),
321            Error::MultipleStatement => write!(f, "Multiple statements provided"),
322            #[cfg(feature = "blob")]
323            Error::BlobSizeError => "Blob size is insufficient".fmt(f),
324            #[cfg(feature = "modern_sqlite")]
325            Error::SqlInputError {
326                ref msg,
327                offset,
328                ref sql,
329                ..
330            } => write!(f, "{msg} in {sql} at offset {offset}"),
331            #[cfg(feature = "loadable_extension")]
332            Error::InitError(ref err) => err.fmt(f),
333            #[cfg(feature = "modern_sqlite")]
334            Error::InvalidDatabaseIndex(i) => write!(f, "Invalid database index: {i}"),
335        }
336    }
337}
338
339impl error::Error for Error {
340    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
341        match *self {
342            Error::SqliteFailure(ref err, _) => Some(err),
343            Error::Utf8Error(ref err) => Some(err),
344            Error::NulError(ref err) => Some(err),
345
346            Error::IntegralValueOutOfRange(..)
347            | Error::SqliteSingleThreadedMode
348            | Error::InvalidParameterName(_)
349            | Error::ExecuteReturnedResults
350            | Error::QueryReturnedNoRows
351            | Error::InvalidColumnIndex(_)
352            | Error::InvalidColumnName(_)
353            | Error::InvalidColumnType(..)
354            | Error::InvalidPath(_)
355            | Error::InvalidParameterCount(..)
356            | Error::StatementChangedRows(_)
357            | Error::InvalidQuery
358            | Error::MultipleStatement => None,
359
360            #[cfg(feature = "functions")]
361            Error::InvalidFunctionParameterType(..) => None,
362            #[cfg(feature = "vtab")]
363            Error::InvalidFilterParameterType(..) => None,
364
365            #[cfg(feature = "functions")]
366            Error::UserFunctionError(ref err) => Some(&**err),
367
368            Error::FromSqlConversionFailure(_, _, ref err)
369            | Error::ToSqlConversionFailure(ref err) => Some(&**err),
370
371            #[cfg(feature = "vtab")]
372            Error::ModuleError(_) => None,
373
374            Error::UnwindingPanic => None,
375
376            #[cfg(feature = "functions")]
377            Error::GetAuxWrongType => None,
378
379            #[cfg(feature = "blob")]
380            Error::BlobSizeError => None,
381            #[cfg(feature = "modern_sqlite")]
382            Error::SqlInputError { ref error, .. } => Some(error),
383            #[cfg(feature = "loadable_extension")]
384            Error::InitError(ref err) => Some(err),
385            #[cfg(feature = "modern_sqlite")]
386            Error::InvalidDatabaseIndex(_) => None,
387        }
388    }
389}
390
391impl Error {
392    /// Returns the underlying SQLite error if this is [`Error::SqliteFailure`].
393    #[inline]
394    #[must_use]
395    pub fn sqlite_error(&self) -> Option<&ffi::Error> {
396        match self {
397            Self::SqliteFailure(error, _) => Some(error),
398            _ => None,
399        }
400    }
401
402    /// Returns the underlying SQLite error code if this is
403    /// [`Error::SqliteFailure`].
404    #[inline]
405    #[must_use]
406    pub fn sqlite_error_code(&self) -> Option<ffi::ErrorCode> {
407        self.sqlite_error().map(|error| error.code)
408    }
409}
410
411// These are public but not re-exported by lib.rs, so only visible within crate.
412
413#[cold]
414pub fn error_from_sqlite_code(code: c_int, message: Option<String>) -> Error {
415    Error::SqliteFailure(ffi::Error::new(code), message)
416}
417
418#[cold]
419pub unsafe fn error_from_handle(db: *mut ffi::sqlite3, code: c_int) -> Error {
420    let message = if db.is_null() {
421        None
422    } else {
423        Some(errmsg_to_string(ffi::sqlite3_errmsg(db)))
424    };
425    error_from_sqlite_code(code, message)
426}
427
428#[cold]
429#[cfg(not(feature = "modern_sqlite"))] // SQLite >= 3.38.0
430pub unsafe fn error_with_offset(db: *mut ffi::sqlite3, code: c_int, _sql: &str) -> Error {
431    error_from_handle(db, code)
432}
433
434#[cold]
435#[cfg(feature = "modern_sqlite")] // SQLite >= 3.38.0
436pub unsafe fn error_with_offset(db: *mut ffi::sqlite3, code: c_int, sql: &str) -> Error {
437    if db.is_null() {
438        error_from_sqlite_code(code, None)
439    } else {
440        let error = ffi::Error::new(code);
441        let msg = errmsg_to_string(ffi::sqlite3_errmsg(db));
442        if ffi::ErrorCode::Unknown == error.code {
443            let offset = ffi::sqlite3_error_offset(db);
444            if offset >= 0 {
445                return Error::SqlInputError {
446                    error,
447                    msg,
448                    sql: sql.to_owned(),
449                    offset,
450                };
451            }
452        }
453        Error::SqliteFailure(error, Some(msg))
454    }
455}
456
457pub fn check(code: c_int) -> Result<()> {
458    if code != crate::ffi::SQLITE_OK {
459        Err(error_from_sqlite_code(code, None))
460    } else {
461        Ok(())
462    }
463}
464
465/// Transform Rust error to SQLite error (message and code).
466/// # Safety
467/// This function is unsafe because it uses raw pointer
468pub unsafe fn to_sqlite_error(e: &Error, err_msg: *mut *mut std::os::raw::c_char) -> c_int {
469    use crate::util::alloc;
470    match e {
471        Error::SqliteFailure(err, s) => {
472            if let Some(s) = s {
473                *err_msg = alloc(s);
474            }
475            err.extended_code
476        }
477        err => {
478            *err_msg = alloc(&err.to_string());
479            ffi::SQLITE_ERROR
480        }
481    }
482}