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