mirror of
https://github.com/isar/rusqlite.git
synced 2024-11-22 07:09:20 +08:00
Serialize and deserialize database
This commit is contained in:
parent
5848c8c147
commit
67d1e34eb4
@ -111,7 +111,6 @@ modern-full = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
bundled-full = ["modern-full", "bundled"]
|
bundled-full = ["modern-full", "bundled"]
|
||||||
default = ["serialize"]
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
time = { version = "0.3.0", features = ["formatting", "macros", "parsing"], optional = true }
|
time = { version = "0.3.0", features = ["formatting", "macros", "parsing"], optional = true }
|
||||||
|
@ -8,7 +8,7 @@ use crate::error::error_from_handle;
|
|||||||
use crate::ffi;
|
use crate::ffi;
|
||||||
use crate::{Connection, DatabaseName, Result};
|
use crate::{Connection, DatabaseName, Result};
|
||||||
|
|
||||||
/// Shared serialized database
|
/// Shared (SQLITE_SERIALIZE_NOCOPY) serialized database
|
||||||
pub struct SharedData<'conn> {
|
pub struct SharedData<'conn> {
|
||||||
phantom: PhantomData<&'conn Connection>,
|
phantom: PhantomData<&'conn Connection>,
|
||||||
ptr: NonNull<u8>,
|
ptr: NonNull<u8>,
|
||||||
@ -21,9 +21,31 @@ pub struct OwnedData {
|
|||||||
sz: usize,
|
sz: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl OwnedData {
|
||||||
|
/// SAFETY: Caller must be certain that `ptr` is allocated by
|
||||||
|
/// `sqlite3_malloc`.
|
||||||
|
pub unsafe fn from_raw_nonnull(ptr: NonNull<u8>, sz: usize) -> Self {
|
||||||
|
Self { ptr, sz }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_raw(self) -> (*mut u8, usize) {
|
||||||
|
let raw = (self.ptr.as_ptr(), self.sz);
|
||||||
|
std::mem::forget(self);
|
||||||
|
raw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for OwnedData {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
ffi::sqlite3_free(self.ptr.as_ptr().cast());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Serialized database
|
/// Serialized database
|
||||||
pub enum Data<'conn> {
|
pub enum Data<'conn> {
|
||||||
/// Shared serialized database
|
/// Shared (SQLITE_SERIALIZE_NOCOPY) serialized database
|
||||||
Shared(SharedData<'conn>),
|
Shared(SharedData<'conn>),
|
||||||
/// Owned serialized database
|
/// Owned serialized database
|
||||||
Owned(OwnedData),
|
Owned(OwnedData),
|
||||||
@ -41,14 +63,6 @@ impl<'conn> Deref for Data<'conn> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for OwnedData {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe {
|
|
||||||
ffi::sqlite3_free(self.ptr.as_ptr().cast());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Connection {
|
impl Connection {
|
||||||
/// Serialize a database.
|
/// Serialize a database.
|
||||||
pub fn serialize<'conn>(&'conn self, schema: DatabaseName<'_>) -> Result<Data<'conn>> {
|
pub fn serialize<'conn>(&'conn self, schema: DatabaseName<'_>) -> Result<Data<'conn>> {
|
||||||
@ -85,34 +99,22 @@ impl Connection {
|
|||||||
pub fn deserialize(
|
pub fn deserialize(
|
||||||
&mut self,
|
&mut self,
|
||||||
schema: DatabaseName<'_>,
|
schema: DatabaseName<'_>,
|
||||||
data: Data<'_>,
|
data: OwnedData,
|
||||||
read_only: bool,
|
read_only: bool,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let schema = schema.as_cstring()?;
|
let schema = schema.as_cstring()?;
|
||||||
let (data, sz, flags) = match data {
|
let (data, sz) = data.into_raw();
|
||||||
Data::Owned(OwnedData { ptr, sz }) => (
|
let sz = sz.try_into().unwrap();
|
||||||
ptr.as_ptr(), // FIXME double-free => mem forget
|
let flags = if read_only {
|
||||||
sz.try_into().unwrap(),
|
|
||||||
if read_only {
|
|
||||||
ffi::SQLITE_DESERIALIZE_FREEONCLOSE | ffi::SQLITE_DESERIALIZE_READONLY
|
ffi::SQLITE_DESERIALIZE_FREEONCLOSE | ffi::SQLITE_DESERIALIZE_READONLY
|
||||||
} else {
|
} else {
|
||||||
ffi::SQLITE_DESERIALIZE_FREEONCLOSE | ffi::SQLITE_DESERIALIZE_RESIZEABLE
|
ffi::SQLITE_DESERIALIZE_FREEONCLOSE | ffi::SQLITE_DESERIALIZE_RESIZEABLE
|
||||||
},
|
|
||||||
),
|
|
||||||
Data::Shared(SharedData { ptr, sz, .. }) => (
|
|
||||||
ptr.as_ptr(), // FIXME lifetime of ptr must be > lifetime self
|
|
||||||
sz.try_into().unwrap(),
|
|
||||||
if read_only {
|
|
||||||
ffi::SQLITE_DESERIALIZE_READONLY
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
},
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
let rc = unsafe {
|
let rc = unsafe {
|
||||||
ffi::sqlite3_deserialize(self.handle(), schema.as_ptr(), data, sz, sz, flags)
|
ffi::sqlite3_deserialize(self.handle(), schema.as_ptr(), data, sz, sz, flags)
|
||||||
};
|
};
|
||||||
if rc != ffi::SQLITE_OK {
|
if rc != ffi::SQLITE_OK {
|
||||||
|
// TODO sqlite3_free(data) ?
|
||||||
return Err(unsafe { error_from_handle(self.handle(), rc) });
|
return Err(unsafe { error_from_handle(self.handle(), rc) });
|
||||||
}
|
}
|
||||||
/* TODO
|
/* TODO
|
||||||
@ -129,3 +131,32 @@ impl Connection {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use crate::{Connection, DatabaseName, Result};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serialize() -> Result<()> {
|
||||||
|
let db = Connection::open_in_memory()?;
|
||||||
|
db.execute_batch("CREATE TABLE x AS SELECT 'data'")?;
|
||||||
|
let data = db.serialize(DatabaseName::Main)?;
|
||||||
|
let Data::Owned(data) = data else { panic!("expected OwnedData")};
|
||||||
|
assert!(data.sz > 0);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deserialize() -> Result<()> {
|
||||||
|
let src = Connection::open_in_memory()?;
|
||||||
|
src.execute_batch("CREATE TABLE x AS SELECT 'data'")?;
|
||||||
|
let data = src.serialize(DatabaseName::Main)?;
|
||||||
|
let Data::Owned(data) = data else { panic!("expected OwnedData")};
|
||||||
|
|
||||||
|
let mut dst = Connection::open_in_memory()?;
|
||||||
|
dst.deserialize(DatabaseName::Main, data, false)?;
|
||||||
|
dst.execute("DELETE FROM x", [])?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user