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