rusqlite/
error.rs

1use crate::types::FromSqlError;
2use crate::types::Type;
3use crate::{errmsg_to_string, ffi};
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 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 [`functions::Context::get`](crate::functions::Context::get) when the function argument
71    /// cannot be converted to the requested type.
72    #[cfg(feature = "functions")]
73    InvalidFunctionParameterType(usize, Type),
74    /// Error returned by [`vtab::Values::get`](crate::vtab::Values::get) when the filter argument cannot
75    /// be converted to the requested type.
76    #[cfg(feature = "vtab")]
77    InvalidFilterParameterType(usize, Type),
78
79    /// An error case available for implementors of custom user functions (e.g.,
80    /// [`create_scalar_function`](crate::Connection::create_scalar_function)).
81    #[cfg(feature = "functions")]
82    #[allow(dead_code)]
83    UserFunctionError(Box<dyn error::Error + Send + Sync + 'static>),
84
85    /// Error available for the implementors of the [`ToSql`](crate::types::ToSql) trait.
86    ToSqlConversionFailure(Box<dyn error::Error + Send + Sync + 'static>),
87
88    /// Error when the SQL is not a `SELECT`, is not read-only.
89    InvalidQuery,
90
91    /// An error case available for implementors of custom modules (e.g.,
92    /// [`create_module`](crate::Connection::create_module)).
93    #[cfg(feature = "vtab")]
94    #[allow(dead_code)]
95    ModuleError(String),
96
97    /// An unwinding panic occurs in an UDF (user-defined function).
98    #[cfg(feature = "functions")]
99    UnwindingPanic,
100
101    /// An error returned when [`Context::get_aux`](crate::functions::Context::get_aux) attempts to retrieve data
102    /// of a different type than what had been stored using [`Context::set_aux`](crate::functions::Context::set_aux).
103    #[cfg(feature = "functions")]
104    GetAuxWrongType,
105
106    /// Error when the SQL contains multiple statements.
107    MultipleStatement,
108    /// Error when the number of bound parameters does not match the number of
109    /// parameters in the query. The first `usize` is how many parameters were
110    /// given, the 2nd is how many were expected.
111    InvalidParameterCount(usize, usize),
112
113    /// Returned from various functions in the Blob IO positional API. For
114    /// example,
115    /// [`Blob::raw_read_at_exact`](crate::blob::Blob::raw_read_at_exact) will
116    /// return it if the blob has insufficient data.
117    #[cfg(feature = "blob")]
118    BlobSizeError,
119}
120
121impl PartialEq for Error {
122    fn eq(&self, other: &Error) -> bool {
123        match (self, other) {
124            (Error::SqliteFailure(e1, s1), Error::SqliteFailure(e2, s2)) => e1 == e2 && s1 == s2,
125            (Error::SqliteSingleThreadedMode, Error::SqliteSingleThreadedMode) => true,
126            (Error::IntegralValueOutOfRange(i1, n1), Error::IntegralValueOutOfRange(i2, n2)) => {
127                i1 == i2 && n1 == n2
128            }
129            (Error::Utf8Error(e1), Error::Utf8Error(e2)) => e1 == e2,
130            (Error::NulError(e1), Error::NulError(e2)) => e1 == e2,
131            (Error::InvalidParameterName(n1), Error::InvalidParameterName(n2)) => n1 == n2,
132            (Error::InvalidPath(p1), Error::InvalidPath(p2)) => p1 == p2,
133            (Error::ExecuteReturnedResults, Error::ExecuteReturnedResults) => true,
134            (Error::QueryReturnedNoRows, Error::QueryReturnedNoRows) => true,
135            (Error::InvalidColumnIndex(i1), Error::InvalidColumnIndex(i2)) => i1 == i2,
136            (Error::InvalidColumnName(n1), Error::InvalidColumnName(n2)) => n1 == n2,
137            (Error::InvalidColumnType(i1, n1, t1), Error::InvalidColumnType(i2, n2, t2)) => {
138                i1 == i2 && t1 == t2 && n1 == n2
139            }
140            (Error::StatementChangedRows(n1), Error::StatementChangedRows(n2)) => n1 == n2,
141            #[cfg(feature = "functions")]
142            (
143                Error::InvalidFunctionParameterType(i1, t1),
144                Error::InvalidFunctionParameterType(i2, t2),
145            ) => i1 == i2 && t1 == t2,
146            #[cfg(feature = "vtab")]
147            (
148                Error::InvalidFilterParameterType(i1, t1),
149                Error::InvalidFilterParameterType(i2, t2),
150            ) => i1 == i2 && t1 == t2,
151            (Error::InvalidQuery, Error::InvalidQuery) => true,
152            #[cfg(feature = "vtab")]
153            (Error::ModuleError(s1), Error::ModuleError(s2)) => s1 == s2,
154            #[cfg(feature = "functions")]
155            (Error::UnwindingPanic, Error::UnwindingPanic) => true,
156            #[cfg(feature = "functions")]
157            (Error::GetAuxWrongType, Error::GetAuxWrongType) => true,
158            (Error::InvalidParameterCount(i1, n1), Error::InvalidParameterCount(i2, n2)) => {
159                i1 == i2 && n1 == n2
160            }
161            #[cfg(feature = "blob")]
162            (Error::BlobSizeError, Error::BlobSizeError) => true,
163            (..) => false,
164        }
165    }
166}
167
168impl From<str::Utf8Error> for Error {
169    #[cold]
170    fn from(err: str::Utf8Error) -> Error {
171        Error::Utf8Error(err)
172    }
173}
174
175impl From<::std::ffi::NulError> for Error {
176    #[cold]
177    fn from(err: ::std::ffi::NulError) -> Error {
178        Error::NulError(err)
179    }
180}
181
182const UNKNOWN_COLUMN: usize = std::usize::MAX;
183
184/// The conversion isn't precise, but it's convenient to have it
185/// to allow use of `get_raw(…).as_…()?` in callbacks that take `Error`.
186impl From<FromSqlError> for Error {
187    #[cold]
188    fn from(err: FromSqlError) -> Error {
189        // The error type requires index and type fields, but they aren't known in this
190        // context.
191        match err {
192            FromSqlError::OutOfRange(val) => Error::IntegralValueOutOfRange(UNKNOWN_COLUMN, val),
193            #[cfg(feature = "i128_blob")]
194            FromSqlError::InvalidI128Size(_) => {
195                Error::FromSqlConversionFailure(UNKNOWN_COLUMN, Type::Blob, Box::new(err))
196            }
197            #[cfg(feature = "uuid")]
198            FromSqlError::InvalidUuidSize(_) => {
199                Error::FromSqlConversionFailure(UNKNOWN_COLUMN, Type::Blob, Box::new(err))
200            }
201            FromSqlError::Other(source) => {
202                Error::FromSqlConversionFailure(UNKNOWN_COLUMN, Type::Null, source)
203            }
204            _ => Error::FromSqlConversionFailure(UNKNOWN_COLUMN, Type::Null, Box::new(err)),
205        }
206    }
207}
208
209impl fmt::Display for Error {
210    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211        match *self {
212            Error::SqliteFailure(ref err, None) => err.fmt(f),
213            Error::SqliteFailure(_, Some(ref s)) => write!(f, "{}", s),
214            Error::SqliteSingleThreadedMode => write!(
215                f,
216                "SQLite was compiled or configured for single-threaded use only"
217            ),
218            Error::FromSqlConversionFailure(i, ref t, ref err) => {
219                if i != UNKNOWN_COLUMN {
220                    write!(
221                        f,
222                        "Conversion error from type {} at index: {}, {}",
223                        t, i, err
224                    )
225                } else {
226                    err.fmt(f)
227                }
228            }
229            Error::IntegralValueOutOfRange(col, val) => {
230                if col != UNKNOWN_COLUMN {
231                    write!(f, "Integer {} out of range at index {}", val, col)
232                } else {
233                    write!(f, "Integer {} out of range", val)
234                }
235            }
236            Error::Utf8Error(ref err) => err.fmt(f),
237            Error::NulError(ref err) => err.fmt(f),
238            Error::InvalidParameterName(ref name) => write!(f, "Invalid parameter name: {}", name),
239            Error::InvalidPath(ref p) => write!(f, "Invalid path: {}", p.to_string_lossy()),
240            Error::ExecuteReturnedResults => {
241                write!(f, "Execute returned results - did you mean to call query?")
242            }
243            Error::QueryReturnedNoRows => write!(f, "Query returned no rows"),
244            Error::InvalidColumnIndex(i) => write!(f, "Invalid column index: {}", i),
245            Error::InvalidColumnName(ref name) => write!(f, "Invalid column name: {}", name),
246            Error::InvalidColumnType(i, ref name, ref t) => write!(
247                f,
248                "Invalid column type {} at index: {}, name: {}",
249                t, i, name
250            ),
251            Error::InvalidParameterCount(i1, n1) => write!(
252                f,
253                "Wrong number of parameters passed to query. Got {}, needed {}",
254                i1, n1
255            ),
256            Error::StatementChangedRows(i) => write!(f, "Query changed {} rows", i),
257
258            #[cfg(feature = "functions")]
259            Error::InvalidFunctionParameterType(i, ref t) => {
260                write!(f, "Invalid function parameter type {} at index {}", t, i)
261            }
262            #[cfg(feature = "vtab")]
263            Error::InvalidFilterParameterType(i, ref t) => {
264                write!(f, "Invalid filter parameter type {} at index {}", t, i)
265            }
266            #[cfg(feature = "functions")]
267            Error::UserFunctionError(ref err) => err.fmt(f),
268            Error::ToSqlConversionFailure(ref err) => err.fmt(f),
269            Error::InvalidQuery => write!(f, "Query is not read-only"),
270            #[cfg(feature = "vtab")]
271            Error::ModuleError(ref desc) => write!(f, "{}", desc),
272            #[cfg(feature = "functions")]
273            Error::UnwindingPanic => write!(f, "unwinding panic"),
274            #[cfg(feature = "functions")]
275            Error::GetAuxWrongType => write!(f, "get_aux called with wrong type"),
276            Error::MultipleStatement => write!(f, "Multiple statements provided"),
277
278            #[cfg(feature = "blob")]
279            Error::BlobSizeError => "Blob size is insufficient".fmt(f),
280        }
281    }
282}
283
284impl error::Error for Error {
285    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
286        match *self {
287            Error::SqliteFailure(ref err, _) => Some(err),
288            Error::Utf8Error(ref err) => Some(err),
289            Error::NulError(ref err) => Some(err),
290
291            Error::IntegralValueOutOfRange(..)
292            | Error::SqliteSingleThreadedMode
293            | Error::InvalidParameterName(_)
294            | Error::ExecuteReturnedResults
295            | Error::QueryReturnedNoRows
296            | Error::InvalidColumnIndex(_)
297            | Error::InvalidColumnName(_)
298            | Error::InvalidColumnType(..)
299            | Error::InvalidPath(_)
300            | Error::InvalidParameterCount(..)
301            | Error::StatementChangedRows(_)
302            | Error::InvalidQuery
303            | Error::MultipleStatement => None,
304
305            #[cfg(feature = "functions")]
306            Error::InvalidFunctionParameterType(..) => None,
307            #[cfg(feature = "vtab")]
308            Error::InvalidFilterParameterType(..) => None,
309
310            #[cfg(feature = "functions")]
311            Error::UserFunctionError(ref err) => Some(&**err),
312
313            Error::FromSqlConversionFailure(_, _, ref err)
314            | Error::ToSqlConversionFailure(ref err) => Some(&**err),
315
316            #[cfg(feature = "vtab")]
317            Error::ModuleError(_) => None,
318
319            #[cfg(feature = "functions")]
320            Error::UnwindingPanic => None,
321
322            #[cfg(feature = "functions")]
323            Error::GetAuxWrongType => None,
324
325            #[cfg(feature = "blob")]
326            Error::BlobSizeError => None,
327        }
328    }
329}
330
331// These are public but not re-exported by lib.rs, so only visible within crate.
332
333#[cold]
334pub fn error_from_sqlite_code(code: c_int, message: Option<String>) -> Error {
335    Error::SqliteFailure(ffi::Error::new(code), message)
336}
337
338#[cold]
339pub unsafe fn error_from_handle(db: *mut ffi::sqlite3, code: c_int) -> Error {
340    let message = if db.is_null() {
341        None
342    } else {
343        Some(errmsg_to_string(ffi::sqlite3_errmsg(db)))
344    };
345    error_from_sqlite_code(code, message)
346}
347
348/// Check SQLite method call.
349/// ```rust,ignore
350/// # use rusqlite::{self, ffi, Result};
351/// fn xyz() -> Result<()> {
352///     
353///     unsafe {
354///        // returns an Error if sqlite3_initialize fails
355///        rusqlite::check!(ffi::sqlite3_initialize());
356///     }
357///     Ok(())
358/// }
359/// ```
360#[macro_export]
361macro_rules! check {
362    ($funcall:expr) => {{
363        let rc = $funcall;
364        if rc != crate::ffi::SQLITE_OK {
365            return Err(crate::error::error_from_sqlite_code(rc, None).into());
366        }
367    }};
368}
369
370/// Transform Rust error to SQLite error (message and code).
371/// # Safety
372/// This function is unsafe because it uses raw pointer
373#[cfg(any(
374    feature = "vtab",
375    feature = "loadable_extension",
376    feature = "loadable_extension_embedded"
377))]
378pub unsafe fn to_sqlite_error(
379    e: &Error,
380    err_msg: *mut *mut std::os::raw::c_char,
381) -> std::os::raw::c_int {
382    use crate::util::alloc;
383    match e {
384        Error::SqliteFailure(err, s) => {
385            if let Some(s) = s {
386                *err_msg = alloc(&s);
387            }
388            err.extended_code
389        }
390        err => {
391            *err_msg = alloc(&err.to_string());
392            ffi::SQLITE_ERROR
393        }
394    }
395}