-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(engine): rewrite database handler
cc #1
- Loading branch information
Huo Linhe
committed
Mar 4, 2016
1 parent
1bcd4cd
commit 63cf25a
Showing
6 changed files
with
309 additions
and
220 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
use super::UnQlite; | ||
use std::ffi::CString; | ||
use std::ptr; | ||
use std::mem; | ||
use ffi::unqlite_config; | ||
use ffi::constants::{UNQLITE_CONFIG_DISABLE_AUTO_COMMIT, UNQLITE_CONFIG_GET_KV_NAME, | ||
UNQLITE_CONFIG_KV_ENGINE, UNQLITE_CONFIG_MAX_PAGE_CACHE}; | ||
|
||
/// This part of functions is about UnQlite's config options. | ||
/// | ||
/// The list is token from `unqlite_config` function, see also [here] | ||
/// (http://unqlite.org/c_api/unqlite_config.html). | ||
/// | ||
/// # Usage | ||
/// | ||
/// ```ignore | ||
/// let unqlite = UnQlite::create("test.db") | ||
/// .max_page_cache(u32) | ||
/// .disable_auto_commit() | ||
/// .kv_engine("lsm"); | ||
/// | ||
/// println!("KV engine name: {}", unqlite.kv_name()); | ||
/// ``` | ||
impl<'config> UnQlite { | ||
/// Maximum raw pages to cache in memory. | ||
/// | ||
/// This is a simple hint, UnQLite is not forced to honor it. | ||
pub fn max_page_cache(self, max: u32) -> Self { | ||
error_or!(unsafe { unqlite_config(self.db, UNQLITE_CONFIG_MAX_PAGE_CACHE, max) }).unwrap(); | ||
self | ||
} | ||
|
||
/// To diable automatically commit action. | ||
/// | ||
/// > | ||
/// Normally, If `unqlite_close()` is invoked while a transaction is open, the transaction is | ||
/// automatically committed. But, if this option is set, then the transaction is automatically | ||
/// rolled back and you should call `unqlite_commit()` manually to commit all database changes. | ||
/// | ||
pub fn disable_auto_commit(self) -> Self { | ||
error_or!(unsafe { unqlite_config(self.db, UNQLITE_CONFIG_DISABLE_AUTO_COMMIT) }).unwrap(); | ||
self | ||
} | ||
|
||
/// Switch to another Key/Value storage engine. | ||
/// | ||
/// *This option is reserved for future usage.* | ||
pub fn kv_engine(self, name: CString) -> Self { | ||
error_or!(unsafe { unqlite_config(self.db, UNQLITE_CONFIG_KV_ENGINE, name.into_raw()) }) | ||
.unwrap(); | ||
self | ||
} | ||
|
||
/// Extract the name of the underlying Key/Value storage engine. | ||
/// | ||
/// Here's some useful names to know: Hash, Mem, R+Tree, LSM, etc. | ||
pub fn kv_name(&self) -> String { | ||
unsafe { | ||
let kv_name: *mut ::libc::c_char = mem::uninitialized(); | ||
error_or!(unqlite_config(self.db, UNQLITE_CONFIG_GET_KV_NAME, &kv_name)).unwrap(); | ||
let len = ::libc::strlen(kv_name); | ||
let (_, vec) = (0..len).fold((kv_name, Vec::new()), |(kv, mut vec), _| { | ||
let u: u8 = ptr::read(kv) as u8; | ||
vec.push(u); | ||
let kv = kv.offset(1); | ||
(kv, vec) | ||
}); | ||
CString::from_vec_unchecked(vec).into_string().unwrap() | ||
} | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
#[cfg(feature = "enable-threads")] | ||
mod tests { | ||
use super::super::UnQlite; | ||
|
||
#[test] | ||
fn test_config() { | ||
let unqlite = UnQlite::create_in_memory().max_page_cache(512000000); | ||
let kv_name = unqlite.kv_name(); | ||
assert_eq!(kv_name, String::from("mem")); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
use std::mem; | ||
use std::ffi::CString; | ||
use ffi::{unqlite_close, unqlite_open}; | ||
|
||
pub use self::openmode::*; | ||
/// | ||
pub struct UnQlite { | ||
db: *mut ::ffi::unqlite, | ||
} | ||
|
||
unsafe impl Send for UnQlite {} | ||
unsafe impl Sync for UnQlite {} | ||
|
||
impl Default for UnQlite { | ||
fn default() -> UnQlite { | ||
UnQlite { db: unsafe { mem::uninitialized() } } | ||
} | ||
} | ||
|
||
impl<'open> UnQlite { | ||
/// Create UnQlite database at specific path. | ||
/// | ||
/// ```ignore | ||
/// let _ = UnQlite::open("str"); | ||
/// let _ = UnQlite::open(String::new()); | ||
/// ``` | ||
#[inline] | ||
fn open<P: AsRef<str>>(filename: P, mode: OpenMode) -> ::Result<UnQlite> { | ||
unsafe { | ||
let mut unqlite = UnQlite::default(); | ||
let filename = filename.as_ref(); | ||
let filename = try!(CString::new(filename)); | ||
error_or!(unqlite_open(&mut unqlite.db, filename.as_ptr(), mode.into()), | ||
unqlite) | ||
} | ||
} | ||
|
||
/// Create UnQlite database as `filename`. | ||
/// | ||
/// By default, the database is created in read-write mode. | ||
/// | ||
/// ## Panics | ||
/// | ||
/// Will panic if failed in creating. | ||
/// | ||
/// ## Example | ||
/// | ||
/// ```ignore | ||
/// let _ = UnQlite::create("test.db"); | ||
/// ``` | ||
/// | ||
/// ## C | ||
/// | ||
/// ```c | ||
/// unqlite *pDb; | ||
/// | ||
/// // in-disk database | ||
/// rc = unqlite_open(&pDb,"test.db",UNQLITE_OPEN_CREATE); | ||
/// | ||
/// // in-memory database | ||
/// rc = unqlite_open(&pDb, ":mem:", UNQLITE_OPEN_MEM); | ||
/// ``` | ||
#[inline] | ||
pub fn create<P: AsRef<str>>(filename: P) -> UnQlite { | ||
Self::open(filename, OpenMode::Create).unwrap() | ||
} | ||
|
||
/// Create database in memory. | ||
/// | ||
/// Equivalent to: | ||
/// | ||
/// ```ignore | ||
/// let _ = UnQlite::create(":mem:"); | ||
/// ``` | ||
#[inline] | ||
pub fn create_in_memory() -> UnQlite { | ||
Self::create(":mem:") | ||
} | ||
|
||
/// A private, temporary on-disk database will be created. | ||
/// | ||
/// This private database will be automatically deleted as soon as | ||
/// the database connection is closed. | ||
/// | ||
/// ## C | ||
/// | ||
/// ```c | ||
/// int rc = unqlite_open("test.db", UNQLITE_OPEN_TEMP_DB); | ||
/// ``` | ||
#[inline] | ||
pub fn create_temp() -> UnQlite { | ||
Self::open("", OpenMode::TempDB).unwrap() | ||
} | ||
|
||
/// Obtain a read-only memory view of the whole database. | ||
/// | ||
/// You will get significant performance improvements with this combination but your database | ||
/// is still read-only. | ||
/// | ||
/// ## Panics | ||
/// | ||
/// Panic if open failed. | ||
/// | ||
/// ## C | ||
/// | ||
/// ```c | ||
/// unqlite_open(&pDb, "test.db", UNQLITE_OPEN_MMAP | UNQLITE_OPEN_READONLY); | ||
/// ``` | ||
#[inline] | ||
pub fn open_mmap<P: AsRef<str>>(filename: P) -> UnQlite { | ||
Self::open(filename, OpenMode::MMap).unwrap() | ||
} | ||
|
||
/// Open the database in a read-only mode. | ||
/// | ||
/// That is, you cannot perform a store, append, commit or rollback operations with this | ||
/// control flag. | ||
/// | ||
/// Always prefer to use `open_mmap` for readonly in disk database. | ||
/// | ||
/// ## Panics | ||
/// | ||
/// Panic too. | ||
/// | ||
/// ## C | ||
/// ```c | ||
/// unqlite_open(&pDb, "test.db", UNQLITE_OPEN_READONLY); | ||
/// ``` | ||
#[inline] | ||
pub fn open_readonly<P: AsRef<str>>(filename: P) -> UnQlite { | ||
Self::open(filename, OpenMode::ReadOnly).unwrap() | ||
} | ||
|
||
fn close(&mut self) -> ::Result<()> { | ||
unsafe { error_or!(unqlite_close(self.db)) } | ||
} | ||
} | ||
|
||
impl Drop for UnQlite { | ||
fn drop(&mut self) { | ||
self.close().unwrap(); | ||
} | ||
} | ||
|
||
pub use self::config::*; | ||
|
||
mod openmode; | ||
mod config; | ||
|
||
#[cfg(test)] | ||
#[cfg(feature = "enable-threads")] | ||
mod tests_threadsafe { | ||
use super::UnQlite; | ||
|
||
#[test] | ||
fn create_temp() { | ||
let _ = UnQlite::create_temp(); | ||
} | ||
|
||
#[test] | ||
fn create_in_memory() { | ||
let _ = UnQlite::create_in_memory(); | ||
} | ||
|
||
#[test] | ||
fn from_readonly_memory() { | ||
let _ = UnQlite::open_readonly(":mem:"); | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::UnQlite; | ||
|
||
#[test] | ||
fn open() { | ||
let _ = UnQlite::create_temp(); | ||
let _ = UnQlite::create_in_memory(); | ||
let _ = UnQlite::open_readonly(":mem:"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
use ffi::constants; | ||
#[derive(Clone, Copy, Debug, PartialEq, Eq)] | ||
pub enum OpenMode { | ||
ReadOnly, | ||
ReadWrite, | ||
Create, | ||
Exclusive, | ||
TempDB, | ||
NoMutex, | ||
OmitJournaling, | ||
InMemory, | ||
MMap, | ||
} | ||
|
||
pub use self::OpenMode::*; | ||
|
||
impl Into<u32> for OpenMode { | ||
fn into(self) -> u32 { | ||
match self { | ||
ReadOnly => constants::UNQLITE_OPEN_READONLY, | ||
ReadWrite => constants::UNQLITE_OPEN_READWRITE, | ||
Create => constants::UNQLITE_OPEN_CREATE, | ||
Exclusive => constants::UNQLITE_OPEN_EXCLUSIVE, | ||
TempDB => constants::UNQLITE_OPEN_TEMP_DB, | ||
NoMutex => constants::UNQLITE_OPEN_NOMUTEX, | ||
OmitJournaling => constants::UNQLITE_OPEN_OMIT_JOURNALING, | ||
InMemory => constants::UNQLITE_OPEN_IN_MEMORY, | ||
MMap => constants::UNQLITE_OPEN_MMAP | constants::UNQLITE_OPEN_READONLY, | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.