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, QueryResult};
9use alloc::boxed::Box;
10use alloc::string::ToString;
11use core::ops::Deref;
12use core::ptr::NonNull;
13
14/// Owns a database serialization returned by `sqlite3_serialize` and releases any allocated buffer with `sqlite3_free`.
15#[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)]
16pub struct SerializedDatabase {
17    state: State,
18}
19
20#[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)]
21enum State {
22    /// A successful serialization owning a SQLite allocation.
23    Owned { data: NonNull<u8>, len: i64 },
24    /// A valid serialization of an empty deserialized database.
25    Empty,
26    /// SQLite failed to allocate the output buffer.
27    AllocationFailed,
28}
29
30impl SerializedDatabase {
31    /// Creates a new `SerializedDatabase` with the given data pointer and length.
32    ///
33    /// # Safety
34    ///
35    /// `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.
36    pub(crate) unsafe fn new(data: NonNull<u8>, len: i64) -> Self {
37        Self {
38            state: State::Owned { data, len },
39        }
40    }
41
42    pub(crate) fn empty() -> Self {
43        Self {
44            state: State::Empty,
45        }
46    }
47
48    pub(crate) fn allocation_failed() -> Self {
49        Self {
50            state: State::AllocationFailed,
51        }
52    }
53
54    /// Returns a slice of the serialized database.
55    ///
56    /// # Panics
57    ///
58    /// Panics if SQLite failed to allocate the buffer holding the serialized database, or if it reported a size this platform cannot address.
59    #[deprecated(note = "Use `SerializedDatabase::try_as_slice` instead")]
60    #[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))]
61    pub fn as_slice(&self) -> &[u8] {
62        self.expect_slice()
63    }
64
65    /// Returns a slice of the serialized database, the out of memory error if
66    /// SQLite failed to allocate the buffer holding it, or a conversion error
67    /// if SQLite reported a size this platform cannot address.
68    pub fn try_as_slice(&self) -> QueryResult<&[u8]> {
69        match self.state {
70            State::Owned { data, len } => {
71                // `from_raw_parts` requires a length no larger than `isize::MAX`.
72                let len = isize::try_from(len).map_err(Error::IntegerConversion)?;
73                let len = usize::try_from(len).map_err(Error::IntegerConversion)?;
74                // SAFETY: `new` guarantees an exclusively owned immutable allocation of `len` initialized bytes for every addressable `len`.
75                Ok(unsafe { core::slice::from_raw_parts(data.as_ptr(), len) })
76            }
77            State::Empty => Ok(&[]),
78            State::AllocationFailed => Err(Error::DatabaseError(
79                DatabaseErrorKind::Unknown,
80                Box::new("out of memory".to_string()),
81            )),
82        }
83    }
84
85    fn expect_slice(&self) -> &[u8] {
86        match self.try_as_slice() {
87            Ok(slice) => slice,
88            Err(e) => {
    ::core::panicking::panic_fmt(format_args!("Cannot access the serialized database: {0}",
            e));
}panic!("Cannot access the serialized database: {e}"),
89        }
90    }
91}
92
93impl Deref for SerializedDatabase {
94    type Target = [u8];
95
96    /// Returns a slice of the serialized database.
97    ///
98    /// Panics if SQLite failed to allocate the buffer holding the serialized
99    /// database or reported a size this platform cannot address, use
100    /// [`try_as_slice`](Self::try_as_slice) to handle those failures instead.
101    fn deref(&self) -> &Self::Target {
102        self.expect_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::{SerializedDatabase, ffi};
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}