diff --git a/src/db.rs b/src/db.rs index 256f12f..fe53e22 100644 --- a/src/db.rs +++ b/src/db.rs @@ -14,7 +14,7 @@ // -use {DB, Error, Options, WriteOptions, ColumnFamily}; +use {DB, Error, Options, WriteOptions, ColumnFamily, ColumnFamilyDescriptor}; use ffi; use ffi_util::opt_bytes_to_ptr; @@ -572,6 +572,16 @@ impl<'a> Drop for Snapshot<'a> { } } +impl ColumnFamilyDescriptor { + // Create a new column family descriptor with the specified name and options. + pub fn new(name: S, options: Options) -> Self where S: Into { + ColumnFamilyDescriptor { + name: name.into(), + options + } + } +} + impl DB { /// Open a database with default options. pub fn open_default>(path: P) -> Result { @@ -585,14 +595,17 @@ impl DB { DB::open_cf(opts, path, &[]) } - /// Open a database with specified options and column family. - /// - /// A column family must be created first by calling `DB::create_cf`. - /// - /// # Panics + /// Open a database with the given database options and column family names. /// - /// * Panics if the column family doesn't exist. + /// Column families opened using this function will be created with default `Options`. pub fn open_cf>(opts: &Options, path: P, cfs: &[&str]) -> Result { + let cfs_v = cfs.to_vec().iter().map(|name| ColumnFamilyDescriptor::new(*name, Options::default())).collect(); + + DB::open_cf_descriptors(opts, path, cfs_v) + } + + /// Open a database with the given database options and column family names/options. + pub fn open_cf_descriptors>(opts: &Options, path: P, cfs: Vec) -> Result { let path = path.as_ref(); let cpath = match CString::new(path.to_string_lossy().as_bytes()) { Ok(c) => c, @@ -621,17 +634,19 @@ impl DB { db = ffi_try!(ffi::rocksdb_open(opts.inner, cpath.as_ptr() as *const _,)); } } else { - let mut cfs_v = cfs.to_vec(); + let mut cfs_v = cfs; // Always open the default column family. - if !cfs_v.contains(&"default") { - cfs_v.push("default"); + if !cfs_v.iter().any(|cf| cf.name == "default") { + cfs_v.push(ColumnFamilyDescriptor { + name: String::from("default"), + options: Options::default() + }); } - // We need to store our CStrings in an intermediate vector // so that their pointers remain valid. let c_cfs: Vec = cfs_v .iter() - .map(|cf| CString::new(cf.as_bytes()).unwrap()) + .map(|cf| CString::new(cf.name.as_bytes()).unwrap()) .collect(); let mut cfnames: Vec<_> = c_cfs.iter().map(|cf| cf.as_ptr()).collect(); @@ -639,10 +654,8 @@ impl DB { // These handles will be populated by DB. let mut cfhandles: Vec<_> = cfs_v.iter().map(|_| ptr::null_mut()).collect(); - // TODO(tyler) allow options to be passed in. - let mut cfopts: Vec<_> = cfs_v - .iter() - .map(|_| unsafe { ffi::rocksdb_options_create() as *const _ }) + let mut cfopts: Vec<_> = cfs_v.iter() + .map(|cf| cf.options.inner as *const _) .collect(); unsafe { @@ -666,7 +679,7 @@ impl DB { } for (n, h) in cfs_v.iter().zip(cfhandles) { - cf_map.insert(n.to_string(), ColumnFamily { inner: h }); + cf_map.insert(n.name.clone(), ColumnFamily { inner: h }); } } diff --git a/src/db_options.rs b/src/db_options.rs index 45eefb1..3cefffe 100644 --- a/src/db_options.rs +++ b/src/db_options.rs @@ -147,6 +147,25 @@ impl Options { } } + /// If true, any column families that didn't exist when opening the database + /// will be created. + /// + /// Default: `false` + /// + /// # Example + /// + /// ``` + /// use rocksdb::Options; + /// + /// let mut opts = Options::default(); + /// opts.create_missing_column_families(true); + /// ``` + pub fn create_missing_column_families(&mut self, create_missing_cfs: bool) { + unsafe { + ffi::rocksdb_options_set_create_missing_column_families(self.inner, create_missing_cfs as c_uchar); + } + } + /// Sets the compression algorithm that will be used for the bottommost level that /// contain files. If level-compaction is used, this option will only affect /// levels after base level. diff --git a/src/lib.rs b/src/lib.rs index 3180264..6cdae3a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,6 +30,21 @@ //! db.delete(b"my key").unwrap(); //! ``` //! +//! Opening a database and a single column family with custom options: +//! +//! ``` +//! use rocksdb::{DB, ColumnFamilyDescriptor, Options}; +//! let mut cf_opts = Options::default(); +//! cf_opts.set_max_write_buffer_number(16); +//! let cf = ColumnFamilyDescriptor::new("cf1", cf_opts); +//! +//! let mut db_opts = Options::default(); +//! db_opts.create_missing_column_families(true); +//! db_opts.create_if_missing(true); +//! +//! let db = DB::open_cf_descriptors(&db_opts, "path/for/rocksdb/storage_with_cfs", vec![cf]).unwrap(); +//! ``` +//! extern crate libc; extern crate librocksdb_sys as ffi; @@ -64,6 +79,14 @@ pub struct DB { path: PathBuf, } +/// A descriptor for a RocksDB column family. +/// +/// A description of the column family, containing the name and `Options`. +pub struct ColumnFamilyDescriptor { + name: String, + options: Options, +} + /// A simple wrapper round a string, used for errors reported from /// ffi calls. #[derive(Debug, Clone, PartialEq)] diff --git a/tests/test_column_family.rs b/tests/test_column_family.rs index 85342a9..97ac54f 100644 --- a/tests/test_column_family.rs +++ b/tests/test_column_family.rs @@ -14,7 +14,7 @@ // extern crate rocksdb; -use rocksdb::{DB, MergeOperands, Options}; +use rocksdb::{DB, MergeOperands, Options, ColumnFamilyDescriptor}; #[test] pub fn test_column_family() { @@ -91,6 +91,25 @@ pub fn test_column_family() { assert!(DB::destroy(&Options::default(), path).is_ok()); } +#[test] +fn test_create_missing_column_family() { + let path = "_rust_rocksdb_missing_cftest"; + + // should be able to create new column families when opening a new database + { + let mut opts = Options::default(); + opts.create_if_missing(true); + opts.create_missing_column_families(true); + + match DB::open_cf(&opts, path, &["cf1"]) { + Ok(_) => println!("successfully created new column family"), + Err(e) => panic!("failed to create new column family: {}", e), + } + } + + assert!(DB::destroy(&Options::default(), path).is_ok()); +} + #[test] #[ignore] fn test_merge_operator() { @@ -158,3 +177,43 @@ fn test_provided_merge(_: &[u8], } Some(result) } + +#[test] +pub fn test_column_family_with_options() { + let path = "_rust_rocksdb_cf_with_optionstest"; + { + let mut cfopts = Options::default(); + cfopts.set_max_write_buffer_number(16); + let cf_descriptor = ColumnFamilyDescriptor::new("cf1", cfopts); + + let mut opts = Options::default(); + opts.create_if_missing(true); + opts.create_missing_column_families(true); + + let cfs = vec![cf_descriptor]; + match DB::open_cf_descriptors(&opts, path, cfs) { + Ok(_) => println!("created db with column family descriptors succesfully"), + Err(e) => { + panic!("could not create new database with column family descriptors: {}", e); + } + } + } + + { + let mut cfopts = Options::default(); + cfopts.set_max_write_buffer_number(16); + let cf_descriptor = ColumnFamilyDescriptor::new("cf1", cfopts); + + let opts = Options::default(); + let cfs = vec![cf_descriptor]; + + match DB::open_cf_descriptors(&opts, path, cfs) { + Ok(_) => println!("succesfully re-opened database with column family descriptorrs"), + Err(e) => { + panic!("unable to re-open database with column family descriptors: {}", e); + } + } + } + + assert!(DB::destroy(&Options::default(), path).is_ok()); +} \ No newline at end of file