rusqlite/
serialize.rs

1//! Serialize a database.
2use std::marker::PhantomData;
3use std::ops::Deref;
4use std::ptr::NonNull;
5
6use crate::error::{error_from_handle, error_from_sqlite_code};
7use crate::ffi;
8use crate::{Connection, Error, Name, Result};
9
10/// Shared (SQLITE_SERIALIZE_NOCOPY) serialized database
11pub struct SharedData<'conn> {
12    phantom: PhantomData<&'conn Connection>,
13    ptr: NonNull<u8>,
14    sz: usize,
15}
16
17/// Owned serialized database
18pub struct OwnedData {
19    ptr: NonNull<u8>,
20    sz: usize,
21}
22
23impl OwnedData {
24    /// # Safety
25    ///
26    /// Caller must be certain that `ptr` is allocated by `sqlite3_malloc`.
27    pub unsafe fn from_raw_nonnull(ptr: NonNull<u8>, sz: usize) -> Self {
28        Self { ptr, sz }
29    }
30
31    fn into_raw(self) -> (*mut u8, usize) {
32        let raw = (self.ptr.as_ptr(), self.sz);
33        std::mem::forget(self);
34        raw
35    }
36}
37
38impl Drop for OwnedData {
39    fn drop(&mut self) {
40        unsafe {
41            ffi::sqlite3_free(self.ptr.as_ptr().cast());
42        }
43    }
44}
45
46/// Serialized database
47pub enum Data<'conn> {
48    /// Shared (SQLITE_SERIALIZE_NOCOPY) serialized database
49    Shared(SharedData<'conn>),
50    /// Owned serialized database
51    Owned(OwnedData),
52}
53
54impl Deref for Data<'_> {
55    type Target = [u8];
56
57    fn deref(&self) -> &[u8] {
58        let (ptr, sz) = match self {
59            Data::Owned(OwnedData { ptr, sz }) => (ptr.as_ptr(), *sz),
60            Data::Shared(SharedData { ptr, sz, .. }) => (ptr.as_ptr(), *sz),
61        };
62        unsafe { std::slice::from_raw_parts(ptr, sz) }
63    }
64}
65
66impl Connection {
67    /// Serialize a database.
68    pub fn serialize<N: Name>(&self, schema: N) -> Result<Data<'_>> {
69        let schema = schema.as_cstr()?;
70        let mut sz = 0;
71        let mut ptr: *mut u8 = unsafe {
72            ffi::sqlite3_serialize(
73                self.handle(),
74                schema.as_ptr(),
75                &mut sz,
76                ffi::SQLITE_SERIALIZE_NOCOPY,
77            )
78        };
79        Ok(if ptr.is_null() {
80            ptr = unsafe { ffi::sqlite3_serialize(self.handle(), schema.as_ptr(), &mut sz, 0) };
81            if ptr.is_null() {
82                return Err(unsafe { error_from_handle(self.handle(), ffi::SQLITE_NOMEM) });
83            }
84            Data::Owned(OwnedData {
85                ptr: NonNull::new(ptr).unwrap(),
86                sz: sz.try_into().unwrap(),
87            })
88        } else {
89            // shared buffer
90            Data::Shared(SharedData {
91                ptr: NonNull::new(ptr).unwrap(),
92                sz: sz.try_into().unwrap(),
93                phantom: PhantomData,
94            })
95        })
96    }
97
98    /// Deserialize from stream
99    pub fn deserialize_read_exact<N: Name, R: std::io::Read>(
100        &mut self,
101        schema: N,
102        mut read: R,
103        sz: usize,
104        read_only: bool,
105    ) -> Result<()> {
106        let ptr = unsafe { ffi::sqlite3_malloc(sz.try_into().unwrap()) }.cast::<u8>();
107        if ptr.is_null() {
108            return Err(error_from_sqlite_code(ffi::SQLITE_NOMEM, None));
109        }
110        let buf = unsafe { std::slice::from_raw_parts_mut(ptr, sz) };
111        read.read_exact(buf).map_err(|e| {
112            Error::SqliteFailure(
113                ffi::Error {
114                    code: ffi::ErrorCode::CannotOpen,
115                    extended_code: ffi::SQLITE_IOERR,
116                },
117                Some(format!("{e}")),
118            )
119        })?;
120        let ptr = NonNull::new(ptr).unwrap();
121        let data = unsafe { OwnedData::from_raw_nonnull(ptr, sz) };
122        self.deserialize(schema, data, read_only)
123    }
124
125    /// Deserialize `include_bytes` as a read only database
126    pub fn deserialize_bytes<N: Name>(&mut self, schema: N, data: &'static [u8]) -> Result<()> {
127        let sz = data.len().try_into().unwrap();
128        self.deserialize_(
129            schema,
130            data.as_ptr() as *mut _,
131            sz,
132            ffi::SQLITE_DESERIALIZE_READONLY,
133        )
134    }
135
136    /// Deserialize a database.
137    pub fn deserialize<N: Name>(
138        &mut self,
139        schema: N,
140        data: OwnedData,
141        read_only: bool,
142    ) -> Result<()> {
143        let (data, sz) = data.into_raw();
144        let sz = sz.try_into().unwrap();
145        let flags = if read_only {
146            ffi::SQLITE_DESERIALIZE_FREEONCLOSE | ffi::SQLITE_DESERIALIZE_READONLY
147        } else {
148            ffi::SQLITE_DESERIALIZE_FREEONCLOSE | ffi::SQLITE_DESERIALIZE_RESIZEABLE
149        };
150        self.deserialize_(schema, data, sz, flags)
151        /* TODO
152        if let Some(mxSize) = mxSize {
153            unsafe {
154                ffi::sqlite3_file_control(
155                    self.handle(),
156                    schema.as_ptr(),
157                    ffi::SQLITE_FCNTL_SIZE_LIMIT,
158                    &mut mxSize,
159                )
160            };
161        }*/
162    }
163
164    fn deserialize_<N: Name>(
165        &mut self,
166        schema: N,
167        data: *mut u8,
168        sz: ffi::sqlite_int64,
169        flags: std::ffi::c_uint,
170    ) -> Result<()> {
171        let schema = schema.as_cstr()?;
172        let rc = unsafe {
173            ffi::sqlite3_deserialize(self.handle(), schema.as_ptr(), data, sz, sz, flags)
174        };
175        if rc != ffi::SQLITE_OK {
176            return Err(unsafe { error_from_handle(self.handle(), rc) });
177        }
178        Ok(())
179    }
180}
181
182#[cfg(test)]
183mod test {
184    use super::*;
185    use crate::MAIN_DB;
186
187    #[test]
188    fn serialize() -> Result<()> {
189        let db = Connection::open_in_memory()?;
190        db.execute_batch("CREATE TABLE x AS SELECT 'data'")?;
191        let data = db.serialize(MAIN_DB)?;
192        let Data::Owned(data) = data else {
193            panic!("expected OwnedData")
194        };
195        assert!(data.sz > 0);
196        Ok(())
197    }
198
199    #[test]
200    fn deserialize_read_exact() -> Result<()> {
201        let db = Connection::open_in_memory()?;
202        db.execute_batch("CREATE TABLE x AS SELECT 'data'")?;
203        let data = db.serialize(MAIN_DB)?;
204
205        let mut dst = Connection::open_in_memory()?;
206        let read = data.deref();
207        dst.deserialize_read_exact(MAIN_DB, read, read.len(), false)?;
208        dst.execute("DELETE FROM x", [])?;
209        Ok(())
210    }
211
212    #[test]
213    fn deserialize_bytes() -> Result<()> {
214        let data = b"";
215        let mut dst = Connection::open_in_memory()?;
216        dst.deserialize_bytes(MAIN_DB, data)?;
217        Ok(())
218    }
219
220    #[test]
221    fn deserialize() -> Result<()> {
222        let src = Connection::open_in_memory()?;
223        src.execute_batch("CREATE TABLE x AS SELECT 'data'")?;
224        let data = src.serialize(MAIN_DB)?;
225        let Data::Owned(data) = data else {
226            panic!("expected OwnedData")
227        };
228
229        let mut dst = Connection::open_in_memory()?;
230        dst.deserialize(MAIN_DB, data, false)?;
231        dst.execute("DELETE FROM x", [])?;
232        Ok(())
233    }
234}