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