1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
|
use std::{
ffi::{CStr, NulError, c_int},
fmt::{self, Display, Formatter},
str::Utf8Error,
};
use crate::{FromSql, ffi};
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error(transparent)]
Sqlite(#[from] SqliteError),
#[error("input contained no SQL")]
EmptyStatement,
#[error(transparent)]
Nul(#[from] NulError),
#[error(transparent)]
Utf8(#[from] Utf8Error),
#[error("invalid column index {0}")]
InvalidColumn(c_int),
#[error("execute returned results")]
ExecReturnedRows,
#[error("multiple statements provided")]
MultipleStatements,
}
#[derive(Debug)]
pub struct SqliteError {
code: c_int,
msg: Option<String>,
}
impl Error {
pub(crate) fn from_code(code: c_int) -> Self {
SqliteError { code, msg: None }.into()
}
pub(crate) fn from_db(db: *mut ffi::sqlite3) -> Self {
// SAFETY: sqlite has checks to handle if db is null or dangling, so these shouldn't cause
// ub for any input
let (code, c_msg) = unsafe { (ffi::sqlite3_errcode(db), ffi::sqlite3_errmsg(db)) };
let msg = if c_msg.is_null() {
None
} else {
Some(
// SAFETY: as long as c_msg is non-null, sqlite shouldn't be giving us bad strings
unsafe { CStr::from_ptr(c_msg) }
.to_string_lossy()
.to_string(),
)
};
SqliteError { code, msg }.into()
}
}
fn errstr(code: c_int) -> &'static str {
// SAFETY: `sqlite3_errstr` always returns a valid null-terminated static string
unsafe { CStr::from_ptr(ffi::sqlite3_errstr(code)) }
.to_str()
.expect("sqlite errors should be valid utf8")
}
impl Display for SqliteError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let msg = self.msg.as_deref().unwrap_or(errstr(self.code));
write!(f, "{msg} ({})", self.code)
}
}
impl std::error::Error for SqliteError {}
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, thiserror::Error)]
pub enum GetError<T: FromSql> {
#[error(transparent)]
Sqlite(#[from] Error),
#[error(transparent)]
FromSql(T::Error),
}
pub type GetResult<T> = std::result::Result<T, GetError<T>>;
|