diff --git a/src/rocksdb.rs b/src/rocksdb.rs index 00a3729..5c0974e 100644 --- a/src/rocksdb.rs +++ b/src/rocksdb.rs @@ -362,6 +362,40 @@ impl RocksDB { } } + pub fn get_cf(&self, cf_name: &str, key: &[u8]) -> RocksDBResult { + let cf = self.cfs.get(cf_name); + if cf.is_none() { + return RocksDBResult::Error(format!("Invalid column family: {}", cf_name).to_string()); + } + unsafe { + let readopts = rocksdb_ffi::rocksdb_readoptions_create(); + if readopts.0.is_null() { + return RocksDBResult::Error("Unable to create rocksdb read \ + options. This is a fairly trivial call, and its failure \ + may be indicative of a mis-compiled or mis-loaded rocksdb \ + library.".to_string()); + } + + let val_len: size_t = 0; + let val_len_ptr = &val_len as *const size_t; + let mut err: *const i8 = 0 as *const i8; + let err_ptr: *mut *const i8 = &mut err; + let val = rocksdb_ffi::rocksdb_get_cf(self.inner, readopts.clone(), + *cf.unwrap(), key.as_ptr(), key.len() as size_t, val_len_ptr, + err_ptr) as *mut u8; + rocksdb_ffi::rocksdb_readoptions_destroy(readopts); + if !err.is_null() { + return RocksDBResult::Error(error_message(err)); + } + match val.is_null() { + true => RocksDBResult::None, + false => { + RocksDBResult::Some(RocksDBVector::from_c(val, val_len)) + } + } + } + } + pub fn create_cf(&mut self, name: &str, opts: &Options) -> Result<(), String> { let cname = match CString::new(name.as_bytes()) { Ok(c) => c, @@ -802,52 +836,3 @@ fn iterator_test() { let opts = Options::new(); assert!(RocksDB::destroy(&opts, path).is_ok()); } - -#[test] -pub fn test_column_family() { - let path = "_rust_rocksdb_cftest"; - - // should be able to create column families - { - let mut db = RocksDB::open_default(path).unwrap(); - let opts = Options::new(); - match db.create_cf("cf1", &opts) { - Ok(_) => println!("cf1 created successfully"), - Err(e) => { - panic!("could not create column family: {}", e); - }, - } - } - - // should fail to open db without specifying same column families - { - match RocksDB::open_default(path) { - Ok(_) => panic!("should not have opened DB successfully without specifying column - families"), - Err(e) => assert!(e.starts_with("Invalid argument: You have to open all column families.")), - } - } - - // should properly open db when specyfing all column families - { - match RocksDB::open_cf(&Options::new(), path, &["cf1"]) { - Ok(_) => println!("successfully opened db with column family"), - Err(e) => panic!("failed to open db with column family: {}", e), - } - - } - // should be able to write, read, merge, batch, and iterate over a cf - { - - } - // should b able to drop a cf - { - let mut db = RocksDB::open_cf(&Options::new(), path, &["cf1"]).unwrap(); - match db.drop_cf("cf1") { - Ok(_) => println!("cf1 successfully dropped."), - Err(e) => panic!("failed to drop column family: {}", e), - } - } - - assert!(RocksDB::destroy(&Options::new(), path).is_ok()); -} diff --git a/test/test_column_family.rs b/test/test_column_family.rs index b79309d..8935596 100644 --- a/test/test_column_family.rs +++ b/test/test_column_family.rs @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -use rocksdb::{Options, RocksDB, Writable, Direction}; +use rocksdb::{Options, RocksDB, RocksDBResult, Writable, Direction, MergeOperands}; #[test] pub fn test_column_family() { @@ -21,7 +21,10 @@ pub fn test_column_family() { // should be able to create column families { - let mut db = RocksDB::open_default(path).unwrap(); + let mut opts = Options::new(); + opts.create_if_missing(true); + opts.add_merge_operator("test operator", test_provided_merge); + let mut db = RocksDB::open(&opts, path).unwrap(); let opts = Options::new(); match db.create_cf("cf1", &opts) { Ok(_) => println!("cf1 created successfully"), @@ -33,7 +36,9 @@ pub fn test_column_family() { // should fail to open db without specifying same column families { - match RocksDB::open_default(path) { + let mut opts = Options::new(); + opts.add_merge_operator("test operator", test_provided_merge); + match RocksDB::open(&opts, path) { Ok(_) => panic!("should not have opened DB successfully without specifying column families"), Err(e) => assert!(e.starts_with("Invalid argument: You have to open all column families.")), @@ -42,15 +47,49 @@ pub fn test_column_family() { // should properly open db when specyfing all column families { - match RocksDB::open_cf(&Options::new(), path, &["cf1"]) { + let mut opts = Options::new(); + opts.add_merge_operator("test operator", test_provided_merge); + match RocksDB::open_cf(&opts, path, &["cf1"]) { Ok(_) => println!("successfully opened db with column family"), Err(e) => panic!("failed to open db with column family: {}", e), } - } // should be able to write, read, merge, batch, and iterate over a cf { + let mut opts = Options::new(); + opts.add_merge_operator("test operator", test_provided_merge); + let mut db = match RocksDB::open_cf(&opts, path, &["cf1"]) { + Ok(db) => { + println!("successfully opened db with column family"); + db + }, + Err(e) => panic!("failed to open db with column family: {}", e), + }; + assert!(db.put_cf("cf1", b"k1", b"v1").is_ok()); + assert!(db.get_cf("cf1", b"k1").unwrap().to_utf8().unwrap() == "v1"); + let p = db.put_cf("cf1", b"k1", b"a"); + assert!(p.is_ok()); + db.merge_cf("cf1", b"k1", b"b"); + db.merge_cf("cf1", b"k1", b"c"); + db.merge_cf("cf1", b"k1", b"d"); + db.merge_cf("cf1", b"k1", b"efg"); + let m = db.merge_cf("cf1", b"k1", b"h"); + println!("m is {:?}", m); + assert!(m.is_ok()); + db.get(b"k1").map( |value| { + match value.to_utf8() { + Some(v) => + println!("retrieved utf8 value: {}", v), + None => + println!("did not read valid utf-8 out of the db"), + } + }).on_absent( || { println!("value not present!") }) + .on_error( |e| { println!("error reading value")}); //: {", e) }); + let r = db.get_cf("cf1", b"k1"); + assert!(r.unwrap().to_utf8().unwrap() == "abcdefgh"); + assert!(db.delete(b"k1").is_ok()); + assert!(db.get(b"k1").is_none()); } // should b able to drop a cf { @@ -63,3 +102,25 @@ pub fn test_column_family() { assert!(RocksDB::destroy(&Options::new(), path).is_ok()); } + +fn test_provided_merge(new_key: &[u8], + existing_val: Option<&[u8]>, + mut operands: &mut MergeOperands) + -> Vec { + let nops = operands.size_hint().0; + let mut result: Vec = Vec::with_capacity(nops); + match existing_val { + Some(v) => { + for e in v { + result.push(*e); + } + }, + None => (), + } + for op in operands { + for e in op { + result.push(*e); + } + } + result +}