Skip to main content

sqlite3_ext/
types.rs

1use super::{ffi, mutex::SQLiteMutexGuard, sqlite3_require_version, Connection};
2use std::{
3    ffi::CStr,
4    os::raw::{c_char, c_int},
5};
6
7/// Alias for [Error::Sqlite]\([ffi::SQLITE_LOCKED]\).
8pub const SQLITE_LOCKED: Error = Error::Sqlite(ffi::SQLITE_LOCKED, None);
9/// Alias for [Error::Sqlite]\([ffi::SQLITE_NOMEM]\).
10pub const SQLITE_NOMEM: Error = Error::Sqlite(ffi::SQLITE_NOMEM, None);
11/// Alias for [Error::Sqlite]\([ffi::SQLITE_READONLY]\).
12pub const SQLITE_READONLY: Error = Error::Sqlite(ffi::SQLITE_READONLY, None);
13/// Alias for [Error::Sqlite]\([ffi::SQLITE_NOTFOUND]\).
14pub const SQLITE_NOTFOUND: Error = Error::Sqlite(ffi::SQLITE_NOTFOUND, None);
15/// Alias for [Error::Sqlite]\([ffi::SQLITE_EMPTY]\).
16pub const SQLITE_EMPTY: Error = Error::Sqlite(ffi::SQLITE_EMPTY, None);
17/// Alias for [Error::Sqlite]\([ffi::SQLITE_CONSTRAINT]\).
18pub const SQLITE_CONSTRAINT: Error = Error::Sqlite(ffi::SQLITE_CONSTRAINT, None);
19/// Alias for [Error::Sqlite]\([ffi::SQLITE_MISMATCH]\).
20pub const SQLITE_MISMATCH: Error = Error::Sqlite(ffi::SQLITE_MISMATCH, None);
21/// Alias for [Error::Sqlite]\([ffi::SQLITE_MISUSE]\).
22pub const SQLITE_MISUSE: Error = Error::Sqlite(ffi::SQLITE_MISUSE, None);
23/// Alias for [Error::Sqlite]\([ffi::SQLITE_RANGE]\).
24pub const SQLITE_RANGE: Error = Error::Sqlite(ffi::SQLITE_RANGE, None);
25
26#[derive(Clone, Eq, PartialEq)]
27pub enum Error {
28    /// An error returned by SQLite.
29    Sqlite(i32, Option<String>),
30    /// A string received from SQLite contains invalid UTF-8, and cannot be converted to a
31    /// `&str`.
32    Utf8Error(std::str::Utf8Error),
33    /// A string being passed from Rust to SQLite contained an interior nul byte.
34    NulError(std::ffi::NulError),
35    /// Caused by an attempt to use an API that is not supported in the current version of
36    /// SQLite.
37    VersionNotSatisfied(std::os::raw::c_int),
38    /// An arbitrary string error message. This is never generated by SQLite or
39    /// sqlite3_ext, but can be used by consumers of this crate to cause SQLite to fail
40    /// with a particular error message.
41    Module(String),
42    /// The result was not necessary to produce because it is an unchanged column in an
43    /// UPDATE operation. See [ValueRef::nochange](crate::ValueRef::nochange) for details.
44    NoChange,
45}
46
47impl Error {
48    /// Convert the return of an SQLite function into a Result\<()\>. This method properly
49    /// handles the non-error result codes (SQLITE_OK, SQLITE_ROW, and SQLITE_DONE).
50    pub fn from_sqlite(rc: i32) -> Result<()> {
51        match rc {
52            ffi::SQLITE_OK | ffi::SQLITE_ROW | ffi::SQLITE_DONE => Ok(()),
53            _ => Err(Error::Sqlite(rc, None)),
54        }
55    }
56
57    /// Convert the return of an SQLite function into a Result\<()\>, with a complete error
58    /// message. This method is similar to [from_sqlite](Self::from_sqlite), except that it
59    /// retrieves the full error message from SQLite in addition to the error code.
60    pub fn from_sqlite_desc(rc: i32, guard: SQLiteMutexGuard<'_, Connection>) -> Result<()> {
61        unsafe { Self::from_sqlite_desc_unchecked(rc, guard.as_mut_ptr()) }
62    }
63
64    /// Equivalent to [from_sqlite_desc](Self::from_sqlite_desc), but without requiring a
65    /// lock on the database.
66    ///
67    /// # Safety
68    ///
69    /// This method is safe, however it may produce incorrect results if called while a
70    /// lock on the SQLite database is not held.
71    pub unsafe fn from_sqlite_desc_unchecked(rc: i32, conn: *mut ffi::sqlite3) -> Result<()> {
72        match rc {
73            ffi::SQLITE_OK | ffi::SQLITE_ROW | ffi::SQLITE_DONE => Ok(()),
74            rc => {
75                let msg = CStr::from_ptr(ffi::sqlite3_errmsg(conn));
76                let msg = msg.to_str()?.to_owned();
77                Err(Error::Sqlite(rc, Some(msg)))
78            }
79        }
80    }
81
82    pub(crate) fn into_sqlite(self, msg: *mut *mut c_char) -> c_int {
83        match self {
84            Error::Sqlite(code, s) => {
85                if let Some(s) = s {
86                    if let Ok(s) = ffi::str_to_sqlite3(&s) {
87                        unsafe { *msg = s };
88                    }
89                }
90                code
91            }
92            e @ Error::Utf8Error(_)
93            | e @ Error::NulError(_)
94            | e @ Error::VersionNotSatisfied(_)
95            | e @ Error::Module(_)
96            | e @ Error::NoChange => {
97                if !msg.is_null() {
98                    if let Ok(s) = ffi::str_to_sqlite3(&format!("{e}")) {
99                        unsafe { *msg = s };
100                    }
101                }
102                ffi::SQLITE_ERROR
103            }
104        }
105    }
106}
107
108impl From<String> for Error {
109    fn from(msg: String) -> Self {
110        Self::Module(msg)
111    }
112}
113
114impl From<&str> for Error {
115    fn from(msg: &str) -> Self {
116        Self::Module(msg.to_string())
117    }
118}
119
120impl std::fmt::Display for Error {
121    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122        match self {
123            Error::Sqlite(_, Some(desc)) => write!(f, "{desc}"),
124            Error::Sqlite(i, None) => {
125                let errstr: Result<&str> = sqlite3_require_version!(3_007_015, unsafe {
126                    std::ffi::CStr::from_ptr(ffi::sqlite3_errstr(*i))
127                        .to_str()
128                        .map_err(Error::Utf8Error)
129                });
130                match errstr {
131                    Ok(s) => write!(f, "{s}"),
132                    _ => write!(f, "SQLite error {i}"),
133                }
134            }
135            Error::Utf8Error(e) => e.fmt(f),
136            Error::NulError(e) => e.fmt(f),
137            Error::Module(s) => write!(f, "{s}"),
138            Error::VersionNotSatisfied(v) => write!(
139                f,
140                "requires SQLite version {}.{}.{} or above",
141                v / 1_000_000,
142                (v / 1000) % 1000,
143                v % 1000
144            ),
145            Error::NoChange => write!(f, "invalid Error::NoChange"),
146        }
147    }
148}
149
150impl std::fmt::Debug for Error {
151    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
152        match self {
153            Error::Sqlite(i, Some(desc)) => f.debug_tuple("Sqlite").field(&i).field(&desc).finish(),
154            Error::Sqlite(i, None) => {
155                let errstr: Result<&str> = sqlite3_require_version!(3_007_015, unsafe {
156                    std::ffi::CStr::from_ptr(ffi::sqlite3_errstr(*i))
157                        .to_str()
158                        .map_err(Error::Utf8Error)
159                });
160                match errstr {
161                    Ok(s) => f.debug_tuple("Sqlite").field(&i).field(&s).finish(),
162                    _ => f.debug_tuple("Sqlite").field(&i).finish(),
163                }
164            }
165            Error::Utf8Error(e) => f.debug_tuple("Utf8Error").field(&e).finish(),
166            Error::NulError(e) => f.debug_tuple("NulError").field(&e).finish(),
167            Error::Module(s) => f.debug_tuple("Module").field(&s).finish(),
168            Error::VersionNotSatisfied(v) => {
169                f.debug_tuple("VersionNotSatisfied").field(&v).finish()
170            }
171            Error::NoChange => f.debug_tuple("NoChange").finish(),
172        }
173    }
174}
175
176impl std::error::Error for Error {}
177
178impl From<std::str::Utf8Error> for Error {
179    fn from(err: std::str::Utf8Error) -> Self {
180        Self::Utf8Error(err)
181    }
182}
183
184impl From<std::ffi::NulError> for Error {
185    fn from(err: std::ffi::NulError) -> Self {
186        Self::NulError(err)
187    }
188}
189
190pub type Result<T> = std::result::Result<T, Error>;