use crate::ffi;
use crate::ffi_util::to_cstring;
use crate::ops::GetColumnFamilys;
use crate::{
db_iterator::DBRawIterator,
db_options::{OptionsMustOutliveDB, ReadOptions},
handle::Handle,
open_raw::{OpenRaw, OpenRawFFI},
ops, ColumnFamily, Error, Options,
};
use std::collections::BTreeMap;
use std::fmt;
use std::marker::PhantomData;
use std::path::{Path, PathBuf};
pub struct DBWithTTL {
pub(crate) inner: *mut ffi::rocksdb_t,
cfs: BTreeMap<String, ColumnFamily>,
path: PathBuf,
_outlive: Vec<OptionsMustOutliveDB>,
}
impl DBWithTTL {
pub fn path(&self) -> &Path {
self.path.as_path()
}
pub fn create_cf_with_ttl<N: AsRef<str>>(
&mut self,
name: N,
opts: &Options,
ttl: i32,
) -> Result<(), Error> {
let cname = to_cstring(
name.as_ref(),
"Failed to convert path to CString when opening rocksdb",
)?;
unsafe {
let cf_handle = ffi_try!(ffi::rocksdb_create_column_family_with_ttl(
self.handle(),
opts.inner,
cname.as_ptr(),
ttl as libc::c_int,
));
self.get_mut_cfs()
.insert(name.as_ref().to_string(), ColumnFamily::new(cf_handle));
};
Ok(())
}
}
impl Default for TTLOpenDescriptor {
fn default() -> Self {
TTLOpenDescriptor {
ttls: TTLs::Default(-1),
}
}
}
pub enum TTLs {
Default(i32),
Columns(Vec<i32>),
}
pub struct TTLOpenDescriptor {
ttls: TTLs,
}
impl TTLOpenDescriptor {
pub fn by_columns(ttls: Vec<i32>) -> Self {
TTLOpenDescriptor {
ttls: TTLs::Columns(ttls),
}
}
pub fn by_default(ttl: i32) -> Self {
TTLOpenDescriptor {
ttls: TTLs::Default(ttl),
}
}
}
impl ops::Open for DBWithTTL {}
impl ops::OpenCF for DBWithTTL {}
impl OpenRaw for DBWithTTL {
type Pointer = ffi::rocksdb_t;
type Descriptor = TTLOpenDescriptor;
fn open_ffi(input: OpenRawFFI<'_, Self::Descriptor>) -> Result<*mut Self::Pointer, Error> {
let pointer = unsafe {
if input.num_column_families <= 0 {
let ttl = match input.open_descriptor.ttls {
TTLs::Default(ttl) => ttl as libc::c_int,
TTLs::Columns(_) => {
return Err(Error::new(
"Ttls size has to be the same as number of column families".to_owned(),
));
}
};
ffi_try!(ffi::rocksdb_open_with_ttl(input.options, input.path, ttl,))
} else {
let ttls = match input.open_descriptor.ttls {
TTLs::Default(ttl) => (0..input.num_column_families)
.map(|_| ttl as libc::c_int)
.collect::<Vec<_>>(),
TTLs::Columns(ref ttls) => {
let ttls: Vec<_> = ttls.iter().map(|t| *t as libc::c_int).collect();
let is_ttls_match = if input.num_column_families <= 0 {
ttls.len() as i32 == 1
} else {
ttls.len() as i32 == input.num_column_families
};
if !is_ttls_match {
return Err(Error::new(
"Ttls size has to be the same as number of column families"
.to_owned(),
));
}
ttls
}
};
ffi_try!(ffi::rocksdb_open_column_families_with_ttl(
input.options,
input.path,
input.num_column_families,
input.column_family_names,
input.column_family_options,
input.column_family_handles,
ttls.as_ptr(),
))
}
};
Ok(pointer)
}
fn build<I>(
path: PathBuf,
_open_descriptor: Self::Descriptor,
pointer: *mut Self::Pointer,
column_families: I,
outlive: Vec<OptionsMustOutliveDB>,
) -> Result<Self, Error>
where
I: IntoIterator<Item = (String, *mut ffi::rocksdb_column_family_handle_t)>,
{
let cfs: BTreeMap<_, _> = column_families
.into_iter()
.map(|(k, h)| (k, ColumnFamily::new(h)))
.collect();
Ok(DBWithTTL {
inner: pointer,
cfs,
path,
_outlive: outlive,
})
}
}
impl Handle<ffi::rocksdb_t> for DBWithTTL {
fn handle(&self) -> *mut ffi::rocksdb_t {
self.inner
}
}
impl ops::Iterate for DBWithTTL {
fn get_raw_iter<'a: 'b, 'b>(&'a self, readopts: &ReadOptions) -> DBRawIterator<'b> {
unsafe {
DBRawIterator {
inner: ffi::rocksdb_create_iterator(self.inner, readopts.handle()),
db: PhantomData,
}
}
}
}
impl ops::IterateCF for DBWithTTL {
fn get_raw_iter_cf<'a: 'b, 'b>(
&'a self,
cf_handle: &ColumnFamily,
readopts: &ReadOptions,
) -> Result<DBRawIterator<'b>, Error> {
unsafe {
Ok(DBRawIterator {
inner: ffi::rocksdb_create_iterator_cf(
self.inner,
readopts.handle(),
cf_handle.inner,
),
db: PhantomData,
})
}
}
}
impl ops::GetColumnFamilys for DBWithTTL {
fn get_cfs(&self) -> &BTreeMap<String, ColumnFamily> {
&self.cfs
}
fn get_mut_cfs(&mut self) -> &mut BTreeMap<String, ColumnFamily> {
&mut self.cfs
}
}
impl ops::Read for DBWithTTL {}
impl ops::Write for DBWithTTL {}
unsafe impl Send for DBWithTTL {}
unsafe impl Sync for DBWithTTL {}
impl Drop for DBWithTTL {
fn drop(&mut self) {
unsafe {
for cf in self.cfs.values() {
ffi::rocksdb_column_family_handle_destroy(cf.inner);
}
ffi::rocksdb_close(self.inner);
}
}
}
impl fmt::Debug for DBWithTTL {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Read-only RocksDB {{ path: {:?} }}", self.path())
}
}