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