Skip to main content

quantydb_sqlite/
error.rs

1//! Error type for the SQLite reader.
2//!
3//! Hand rolled like the core's, see ADR-008. The invariant this type exists
4//! to serve: every input, including a deliberately hostile one, produces
5//! either correct data or one of these errors. Never a panic, never a wrong
6//! row.
7//!
8//! The split between `NotSqlite`, `Unsupported` and `Malformed` is a
9//! statement about whose problem it is. `NotSqlite` means the file was never
10//! ours to read. `Unsupported` means the file is fine and we are not good
11//! enough yet, so it names the feature and is a to do list for us.
12//! `Malformed` means the file claims to be a SQLite database and then
13//! contradicts itself, which is the case an attacker controls, so those
14//! carry the page number to make a fuzz failure reproducible.
15
16use std::fmt;
17
18pub type Result<T> = std::result::Result<T, SqliteError>;
19
20#[derive(Debug)]
21pub enum SqliteError {
22    Io(std::io::Error),
23    /// Not a SQLite database at all: wrong magic, or too short to hold a
24    /// header.
25    NotSqlite(String),
26    /// A real SQLite database using something this reader cannot handle
27    /// yet. Named, so the message tells the user what to do.
28    Unsupported(String),
29    /// The file is internally inconsistent: pointers out of range, sizes
30    /// that do not add up, values the format forbids.
31    Malformed {
32        page: Option<u32>,
33        reason: String,
34    },
35}
36
37impl SqliteError {
38    pub(crate) fn malformed(page: impl Into<Option<u32>>, reason: impl Into<String>) -> Self {
39        SqliteError::Malformed {
40            page: page.into(),
41            reason: reason.into(),
42        }
43    }
44
45    pub(crate) fn unsupported(reason: impl Into<String>) -> Self {
46        SqliteError::Unsupported(reason.into())
47    }
48
49    pub(crate) fn not_sqlite(reason: impl Into<String>) -> Self {
50        SqliteError::NotSqlite(reason.into())
51    }
52}
53
54impl fmt::Display for SqliteError {
55    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56        match self {
57            SqliteError::Io(e) => write!(f, "io error: {e}"),
58            SqliteError::NotSqlite(reason) => write!(f, "not a sqlite database: {reason}"),
59            SqliteError::Unsupported(reason) => write!(f, "unsupported sqlite file: {reason}"),
60            SqliteError::Malformed {
61                page: Some(p),
62                reason,
63            } => write!(f, "malformed sqlite database (page {p}): {reason}"),
64            SqliteError::Malformed { page: None, reason } => {
65                write!(f, "malformed sqlite database: {reason}")
66            }
67        }
68    }
69}
70
71impl std::error::Error for SqliteError {
72    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
73        match self {
74            SqliteError::Io(e) => Some(e),
75            _ => None,
76        }
77    }
78}
79
80impl From<std::io::Error> for SqliteError {
81    fn from(e: std::io::Error) -> Self {
82        SqliteError::Io(e)
83    }
84}