• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Serialize a database.
2 use std::marker::PhantomData;
3 use std::ops::Deref;
4 use std::ptr::NonNull;
5 
6 use crate::error::error_from_handle;
7 use crate::ffi;
8 use crate::{Connection, DatabaseName, Result};
9 
10 /// Shared (SQLITE_SERIALIZE_NOCOPY) serialized database
11 pub struct SharedData<'conn> {
12     phantom: PhantomData<&'conn Connection>,
13     ptr: NonNull<u8>,
14     sz: usize,
15 }
16 
17 /// Owned serialized database
18 pub struct OwnedData {
19     ptr: NonNull<u8>,
20     sz: usize,
21 }
22 
23 impl OwnedData {
24     /// # Safety
25     ///
26     /// Caller must be certain that `ptr` is allocated by `sqlite3_malloc`.
from_raw_nonnull(ptr: NonNull<u8>, sz: usize) -> Self27     pub unsafe fn from_raw_nonnull(ptr: NonNull<u8>, sz: usize) -> Self {
28         Self { ptr, sz }
29     }
30 
into_raw(self) -> (*mut u8, usize)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 
38 impl Drop for OwnedData {
drop(&mut self)39     fn drop(&mut self) {
40         unsafe {
41             ffi::sqlite3_free(self.ptr.as_ptr().cast());
42         }
43     }
44 }
45 
46 /// Serialized database
47 pub enum Data<'conn> {
48     /// Shared (SQLITE_SERIALIZE_NOCOPY) serialized database
49     Shared(SharedData<'conn>),
50     /// Owned serialized database
51     Owned(OwnedData),
52 }
53 
54 impl<'conn> Deref for Data<'conn> {
55     type Target = [u8];
56 
deref(&self) -> &[u8]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 
66 impl Connection {
67     /// Serialize a database.
serialize(&self, schema: DatabaseName) -> Result<Data>68     pub fn serialize(&self, schema: DatabaseName) -> Result<Data> {
69         let schema = schema.as_cstring()?;
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 a database.
deserialize( &mut self, schema: DatabaseName<'_>, data: OwnedData, read_only: bool, ) -> Result<()>99     pub fn deserialize(
100         &mut self,
101         schema: DatabaseName<'_>,
102         data: OwnedData,
103         read_only: bool,
104     ) -> Result<()> {
105         let schema = schema.as_cstring()?;
106         let (data, sz) = data.into_raw();
107         let sz = sz.try_into().unwrap();
108         let flags = if read_only {
109             ffi::SQLITE_DESERIALIZE_FREEONCLOSE | ffi::SQLITE_DESERIALIZE_READONLY
110         } else {
111             ffi::SQLITE_DESERIALIZE_FREEONCLOSE | ffi::SQLITE_DESERIALIZE_RESIZEABLE
112         };
113         let rc = unsafe {
114             ffi::sqlite3_deserialize(self.handle(), schema.as_ptr(), data, sz, sz, flags)
115         };
116         if rc != ffi::SQLITE_OK {
117             // TODO sqlite3_free(data) ?
118             return Err(unsafe { error_from_handle(self.handle(), rc) });
119         }
120         /* TODO
121         if let Some(mxSize) = mxSize {
122             unsafe {
123                 ffi::sqlite3_file_control(
124                     self.handle(),
125                     schema.as_ptr(),
126                     ffi::SQLITE_FCNTL_SIZE_LIMIT,
127                     &mut mxSize,
128                 )
129             };
130         }*/
131         Ok(())
132     }
133 }
134 
135 #[cfg(test)]
136 mod test {
137     use super::*;
138 
139     #[test]
serialize() -> Result<()>140     fn serialize() -> Result<()> {
141         let db = Connection::open_in_memory()?;
142         db.execute_batch("CREATE TABLE x AS SELECT 'data'")?;
143         let data = db.serialize(DatabaseName::Main)?;
144         let Data::Owned(data) = data else {
145             panic!("expected OwnedData")
146         };
147         assert!(data.sz > 0);
148         Ok(())
149     }
150 
151     #[test]
deserialize() -> Result<()>152     fn deserialize() -> Result<()> {
153         let src = Connection::open_in_memory()?;
154         src.execute_batch("CREATE TABLE x AS SELECT 'data'")?;
155         let data = src.serialize(DatabaseName::Main)?;
156         let Data::Owned(data) = data else {
157             panic!("expected OwnedData")
158         };
159 
160         let mut dst = Connection::open_in_memory()?;
161         dst.deserialize(DatabaseName::Main, data, false)?;
162         dst.execute("DELETE FROM x", [])?;
163         Ok(())
164     }
165 }
166