Skip to content

Commit

Permalink
refactor(engine): rewrite database handler
Browse files Browse the repository at this point in the history
cc #1
  • Loading branch information
Huo Linhe committed Mar 4, 2016
1 parent 1bcd4cd commit 63cf25a
Show file tree
Hide file tree
Showing 6 changed files with 309 additions and 220 deletions.
84 changes: 84 additions & 0 deletions src/engine/config.rs
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"));
}
}
181 changes: 181 additions & 0 deletions src/engine/mod.rs
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:");
}
}
31 changes: 31 additions & 0 deletions src/engine/openmode.rs
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,
}
}
}
16 changes: 11 additions & 5 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,18 @@ pub type Result<T> = result::Result<T, Error>;

#[macro_export]
macro_rules! error_or {
($code: expr) => {
match $code {
::ffi::constants::UNQLITE_OK => Ok(()),
code => Err(::Error::from(code)),
}
};
($code: expr, $ok: expr) => {
match $code {
::ffi::constants::UNQLITE_OK => Ok($ok),
code => Err(Error::from(code))
code => Err(::Error::from(code)),
}
}
};
}

#[derive(Debug)]
Expand Down Expand Up @@ -41,9 +47,9 @@ impl From<i32> for Error {

impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
&Error::UnqliteFailure(ref err) => err.fmt(f),
&Error::NulError(ref err) => err.fmt(f),
match *self {
Error::UnqliteFailure(ref err) => err.fmt(f),
Error::NulError(ref err) => err.fmt(f),
}
}
}
Expand Down
Loading

0 comments on commit 63cf25a

Please sign in to comment.