Skip to main content

diesel/sqlite/connection/
serialized_database.rs

1#![allow(unsafe_code)]
2#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3extern crate libsqlite3_sys as ffi;
4
5#[cfg(all(target_family = "wasm", target_os = "unknown"))]
6use sqlite_wasm_rs as ffi;
7
8use crate::result::{DatabaseErrorKind, Error};
9use crate::QueryResult;
10use std::ops::Deref;
11use std::ptr::NonNull;
12
13/// Owns a database serialization returned by `sqlite3_serialize` and releases any allocated buffer with `sqlite3_free`.
14#[derive(#[automatically_derived]
impl ::core::fmt::Debug for SerializedDatabase {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field1_finish(f,
            "SerializedDatabase", "state", &&self.state)
    }
}Debug)]
15pub struct SerializedDatabase {
16    state: State,
17}
18
19#[derive(#[automatically_derived]
impl ::core::fmt::Debug for State {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            State::Owned { data: __self_0, len: __self_1 } =>
                ::core::fmt::Formatter::debug_struct_field2_finish(f, "Owned",
                    "data", __self_0, "len", &__self_1),
            State::Empty => ::core::fmt::Formatter::write_str(f, "Empty"),
            State::AllocationFailed =>
                ::core::fmt::Formatter::write_str(f, "AllocationFailed"),
        }
    }
}Debug)]
20enum State {
21    /// A successful serialization owning a SQLite allocation.
22    Owned { data: NonNull<u8>, len: i64 },
23    /// A valid serialization of an empty deserialized database.
24    Empty,
25    /// SQLite failed to allocate the output buffer.
26    AllocationFailed,
27}
28
29impl SerializedDatabase {
30    /// Creates a new `SerializedDatabase` with the given data pointer and length.
31    ///
32    /// # Safety
33    ///
34    /// `data` must exclusively own a successful `sqlite3_serialize` allocation that this value may free, and, if `0 <= len <= isize::MAX`, that allocation must hold `len` initialized bytes.
35    pub(crate) unsafe fn new(data: NonNull<u8>, len: i64) -> Self {
36        Self {
37            state: State::Owned { data, len },
38        }
39    }
40
41    pub(crate) fn empty() -> Self {
42        Self {
43            state: State::Empty,
44        }
45    }
46
47    pub(crate) fn allocation_failed() -> Self {
48        Self {
49            state: State::AllocationFailed,
50        }
51    }
52
53    /// Returns a slice of the serialized database.
54    ///
55    /// # Panics
56    ///
57    /// Panics if SQLite failed to allocate the buffer holding the serialized database, or if it reported a size this platform cannot address.
58    #[deprecated(note = "Use `SerializedDatabase::try_as_slice` instead")]
59    #[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))]
60    pub fn as_slice(&self) -> &[u8] {
61        self.expect_slice()
62    }
63
64    #[cfg(not(all(feature = "with-deprecated", not(feature = "without-deprecated"))))]
65    fn as_slice(&self) -> &[u8] {
66        self.expect_slice()
67    }
68
69    /// Returns a slice of the serialized database, the out of memory error if
70    /// SQLite failed to allocate the buffer holding it, or a conversion error
71    /// if SQLite reported a size this platform cannot address.
72    pub fn try_as_slice(&self) -> QueryResult<&[u8]> {
73        match self.state {
74            State::Owned { data, len } => {
75                // `from_raw_parts` requires a length no larger than `isize::MAX`.
76                let len = isize::try_from(len).map_err(Error::IntegerConversion)?;
77                let len = usize::try_from(len).map_err(Error::IntegerConversion)?;
78                // SAFETY: `new` guarantees an exclusively owned immutable allocation of `len` initialized bytes for every addressable `len`.
79                Ok(unsafe { core::slice::from_raw_parts(data.as_ptr(), len) })
80            }
81            State::Empty => Ok(&[]),
82            State::AllocationFailed => Err(Error::DatabaseError(
83                DatabaseErrorKind::Unknown,
84                Box::new("out of memory".to_string()),
85            )),
86        }
87    }
88
89    fn expect_slice(&self) -> &[u8] {
90        match self.try_as_slice() {
91            Ok(slice) => slice,
92            Err(e) => {
    ::core::panicking::panic_fmt(format_args!("Cannot access the serialized database: {0}",
            e));
}panic!("Cannot access the serialized database: {e}"),
93        }
94    }
95}
96
97impl Deref for SerializedDatabase {
98    type Target = [u8];
99
100    #[allow(deprecated)] // no other way to implement this
101    fn deref(&self) -> &Self::Target {
102        self.as_slice()
103    }
104}
105
106impl Drop for SerializedDatabase {
107    /// Deallocates the memory of the serialized database when it goes out of scope.
108    fn drop(&mut self) {
109        if let State::Owned { data, .. } = self.state {
110            // SAFETY: `new` transfers one SQLite allocation that remains owned until this single `Drop` call.
111            unsafe {
112                ffi::sqlite3_free(data.as_ptr() as _);
113            }
114        }
115    }
116}
117
118#[cfg(all(test, target_pointer_width = "32"))]
119mod tests {
120    use super::{ffi, SerializedDatabase};
121    use crate::result::Error;
122    use core::ptr::NonNull;
123
124    // A serialization one byte past `isize::MAX` still fits `usize`, so only an `isize` bound
125    // keeps it from building a slice `from_raw_parts` rejects. Its allocation is still freed.
126    #[diesel_test_helper::test]
127    fn oversized_serialization_is_rejected() {
128        let len = i64::try_from(isize::MAX).expect("`isize::MAX` fits `i64`") + 1;
129        // SAFETY: SQLite's allocator requires no connection and returns an exclusive allocation.
130        let data = unsafe { ffi::sqlite3_malloc(1) };
131        let data = NonNull::new(data.cast::<u8>()).expect("SQLite allocates a single byte");
132        // SAFETY: `data` exclusively owns a SQLite allocation and `len` exceeds `isize::MAX`, so no byte count is required of it.
133        let serialized = unsafe { SerializedDatabase::new(data, len) };
134
135        assert!(matches!(
136            serialized.try_as_slice(),
137            Err(Error::IntegerConversion(_))
138        ));
139    }
140}