1 // Copyright 2016 Alex Regueiro
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 
16 // This code is based on <https://github.com/facebook/rocksdb/blob/master/db/c_test.c>, revision a10e8a056d569acf6a52045124e6414ad33bdfcd.
17 
18 #![allow(
19     non_snake_case,
20     non_upper_case_globals,
21     unused_mut,
22     unused_unsafe,
23     unused_variables
24 )]
25 
26 use const_cstr::const_cstr;
27 use libc::*;
28 use librocksdb_sys::*;
29 use std::borrow::Cow;
30 use std::env;
31 use std::ffi::{CStr, CString};
32 use std::io::Write;
33 use std::mem;
34 use std::path::PathBuf;
35 use std::ptr;
36 use std::slice;
37 use std::str;
38 use uuid::Uuid;
39 
40 macro_rules! err_println {
41     ($($arg:tt)*) => (writeln!(&mut ::std::io::stderr(), $($arg)*).expect("failed printing to stderr"));
42 }
43 
44 macro_rules! cstrp {
45     ($($arg:tt)*) => (const_cstr!($($arg)*).as_ptr());
46 }
47 
48 static mut phase: &'static str = "";
49 // static mut dbname: *mut c_uchar = ptr::null_mut();
50 // static mut dbbackupname: *mut c_uchar = ptr::null_mut();
51 
strndup(s: *const c_char, n: size_t) -> *mut c_char52 unsafe fn strndup(s: *const c_char, n: size_t) -> *mut c_char {
53     let r: *mut c_char = malloc(n + 1) as *mut c_char;
54     if r.is_null() {
55         return r;
56     }
57     strncpy(r, s, n)
58 }
59 
rstr<'a>(s: *const c_char) -> Cow<'a, str>60 unsafe fn rstr<'a>(s: *const c_char) -> Cow<'a, str> {
61     CStr::from_ptr(s).to_string_lossy()
62 }
63 
GetTempDir() -> PathBuf64 fn GetTempDir() -> PathBuf {
65     return match option_env!("TEST_TMPDIR") {
66         Some("") | None => env::temp_dir(),
67         Some(s) => s.into(),
68     };
69 }
70 
StartPhase(name: &'static str)71 unsafe fn StartPhase(name: &'static str) {
72     err_println!("=== Test {}\n", name);
73     phase = name;
74 }
75 
76 macro_rules! CheckNoError {
77     ($err:ident) => {
78         unsafe {
79             assert!($err.is_null(), "{}: {}", phase, rstr($err));
80         }
81     };
82 }
83 
84 macro_rules! CheckCondition {
85     ($cond:expr) => {
86         unsafe {
87             assert!($cond, "{}: {}", phase, stringify!($cond));
88         }
89     };
90 }
91 
CheckEqual(expected: *const c_char, v: *const c_char, n: size_t)92 unsafe fn CheckEqual(expected: *const c_char, v: *const c_char, n: size_t) {
93     if expected.is_null() && v.is_null() {
94         // ok
95     } else if !expected.is_null()
96         && !v.is_null()
97         && n == strlen(expected)
98         && memcmp(expected as *const c_void, v as *const c_void, n) == 0
99     {
100         // ok
101     } else {
102         panic!(
103             "{}: expected '{}', got '{}'",
104             phase,
105             rstr(strndup(expected, n)),
106             rstr(strndup(v, 5))
107         );
108     }
109 }
110 
Free<T>(ptr: *mut *mut T)111 unsafe fn Free<T>(ptr: *mut *mut T) {
112     if !(*ptr).is_null() {
113         free(*ptr as *mut c_void);
114         *ptr = ptr::null_mut();
115     }
116 }
117 
CheckGet( mut db: *mut rocksdb_t, options: *mut rocksdb_readoptions_t, key: *const c_char, expected: *const c_char, )118 unsafe fn CheckGet(
119     mut db: *mut rocksdb_t,
120     options: *mut rocksdb_readoptions_t,
121     key: *const c_char,
122     expected: *const c_char,
123 ) {
124     let mut err: *mut c_char = ptr::null_mut();
125     let mut val_len: size_t = 0;
126     let mut val: *mut c_char = rocksdb_get(db, options, key, strlen(key), &mut val_len, &mut err);
127     CheckNoError!(err);
128     CheckEqual(expected, val, val_len);
129     Free(&mut val);
130 }
131 
CheckGetCF( db: *mut rocksdb_t, options: *const rocksdb_readoptions_t, handle: *mut rocksdb_column_family_handle_t, key: *const c_char, expected: *const c_char, )132 unsafe fn CheckGetCF(
133     db: *mut rocksdb_t,
134     options: *const rocksdb_readoptions_t,
135     handle: *mut rocksdb_column_family_handle_t,
136     key: *const c_char,
137     expected: *const c_char,
138 ) {
139     let mut err: *mut c_char = ptr::null_mut();
140     let mut val_len: size_t = 0;
141     let mut val: *mut c_char = rocksdb_get_cf(
142         db,
143         options,
144         handle,
145         key,
146         strlen(key),
147         &mut val_len,
148         &mut err,
149     );
150     CheckNoError!(err);
151     CheckEqual(expected, val, val_len);
152     Free(&mut val);
153 }
154 
CheckIter(iter: *mut rocksdb_iterator_t, key: *const c_char, val: *const c_char)155 unsafe fn CheckIter(iter: *mut rocksdb_iterator_t, key: *const c_char, val: *const c_char) {
156     let mut len: size_t = 0;
157     let mut str: *const c_char;
158     str = rocksdb_iter_key(iter, &mut len);
159     CheckEqual(key, str, len);
160     str = rocksdb_iter_value(iter, &mut len);
161     CheckEqual(val, str, len);
162 }
163 
164 // Callback from rocksdb_writebatch_iterate()
CheckPut( ptr: *mut c_void, k: *const c_char, klen: size_t, v: *const c_char, vlen: size_t, )165 unsafe extern "C" fn CheckPut(
166     ptr: *mut c_void,
167     k: *const c_char,
168     klen: size_t,
169     v: *const c_char,
170     vlen: size_t,
171 ) {
172     let mut state: *mut c_int = ptr as *mut c_int;
173     CheckCondition!(*state < 2);
174     match *state {
175         0 => {
176             CheckEqual(cstrp!("bar"), k, klen);
177             CheckEqual(cstrp!("b"), v, vlen);
178         }
179         1 => {
180             CheckEqual(cstrp!("box"), k, klen);
181             CheckEqual(cstrp!("c"), v, vlen);
182         }
183         _ => {}
184     }
185     *state += 1;
186 }
187 
188 // Callback from rocksdb_writebatch_iterate()
CheckDel(ptr: *mut c_void, k: *const c_char, klen: size_t)189 unsafe extern "C" fn CheckDel(ptr: *mut c_void, k: *const c_char, klen: size_t) {
190     let mut state: *mut c_int = ptr as *mut c_int;
191     CheckCondition!(*state == 2);
192     CheckEqual(cstrp!("bar"), k, klen);
193     *state += 1;
194 }
195 
CmpDestroy(arg: *mut c_void)196 unsafe extern "C" fn CmpDestroy(arg: *mut c_void) {}
197 
CmpCompare( arg: *mut c_void, a: *const c_char, alen: size_t, b: *const c_char, blen: size_t, ) -> c_int198 unsafe extern "C" fn CmpCompare(
199     arg: *mut c_void,
200     a: *const c_char,
201     alen: size_t,
202     b: *const c_char,
203     blen: size_t,
204 ) -> c_int {
205     let n = if alen < blen { alen } else { blen };
206     let mut r = memcmp(a as *const c_void, b as *const c_void, n);
207     if r == 0 {
208         if alen < blen {
209             r = -1;
210         } else if alen > blen {
211             r = 1;
212         }
213     }
214     r
215 }
216 
CmpName(arg: *mut c_void) -> *const c_char217 unsafe extern "C" fn CmpName(arg: *mut c_void) -> *const c_char {
218     cstrp!("foo")
219 }
220 
221 // Custom filter policy
222 
223 static mut fake_filter_result: c_uchar = 1;
224 
FilterDestroy(arg: *mut c_void)225 unsafe extern "C" fn FilterDestroy(arg: *mut c_void) {}
226 
FilterName(arg: *mut c_void) -> *const c_char227 unsafe extern "C" fn FilterName(arg: *mut c_void) -> *const c_char {
228     cstrp!("TestFilter")
229 }
230 
FilterCreate( arg: *mut c_void, key_array: *const *const c_char, key_length_array: *const size_t, num_keys: c_int, filter_length: *mut size_t, ) -> *mut c_char231 unsafe extern "C" fn FilterCreate(
232     arg: *mut c_void,
233     key_array: *const *const c_char,
234     key_length_array: *const size_t,
235     num_keys: c_int,
236     filter_length: *mut size_t,
237 ) -> *mut c_char {
238     *filter_length = 4;
239     let result = malloc(4);
240     memcpy(result, cstrp!("fake") as *const c_void, 4);
241     result as *mut c_char
242 }
243 
FilterKeyMatch( arg: *mut c_void, key: *const c_char, length: size_t, filter: *const c_char, filter_length: size_t, ) -> c_uchar244 unsafe extern "C" fn FilterKeyMatch(
245     arg: *mut c_void,
246     key: *const c_char,
247     length: size_t,
248     filter: *const c_char,
249     filter_length: size_t,
250 ) -> c_uchar {
251     CheckCondition!(filter_length == 4);
252     CheckCondition!(
253         memcmp(
254             filter as *const c_void,
255             cstrp!("fake") as *const c_void,
256             filter_length
257         ) == 0
258     );
259     fake_filter_result
260 }
261 
262 // Custom compaction filter
263 
CFilterDestroy(arg: *mut c_void)264 unsafe extern "C" fn CFilterDestroy(arg: *mut c_void) {}
265 
CFilterName(arg: *mut c_void) -> *const c_char266 unsafe extern "C" fn CFilterName(arg: *mut c_void) -> *const c_char {
267     cstrp!("foo")
268 }
269 
CFilterFilter( arg: *mut c_void, level: c_int, key: *const c_char, key_length: size_t, existing_value: *const c_char, value_length: size_t, new_value: *mut *mut c_char, new_value_length: *mut size_t, value_changed: *mut u8, ) -> c_uchar270 unsafe extern "C" fn CFilterFilter(
271     arg: *mut c_void,
272     level: c_int,
273     key: *const c_char,
274     key_length: size_t,
275     existing_value: *const c_char,
276     value_length: size_t,
277     new_value: *mut *mut c_char,
278     new_value_length: *mut size_t,
279     value_changed: *mut u8,
280 ) -> c_uchar {
281     if key_length == 3 {
282         if memcmp(
283             mem::transmute(key),
284             mem::transmute(cstrp!("bar")),
285             key_length,
286         ) == 0
287         {
288             return 1;
289         } else if memcmp(
290             mem::transmute(key),
291             mem::transmute(cstrp!("baz")),
292             key_length,
293         ) == 0
294         {
295             *value_changed = 1;
296             *new_value = cstrp!("newbazvalue") as *mut c_char;
297             *new_value_length = 11;
298             return 0;
299         }
300     }
301     0
302 }
303 
CFilterFactoryDestroy(arg: *mut c_void)304 unsafe extern "C" fn CFilterFactoryDestroy(arg: *mut c_void) {}
305 
CFilterFactoryName(arg: *mut c_void) -> *const c_char306 unsafe extern "C" fn CFilterFactoryName(arg: *mut c_void) -> *const c_char {
307     cstrp!("foo")
308 }
309 
CFilterCreate( arg: *mut c_void, context: *mut rocksdb_compactionfiltercontext_t, ) -> *mut rocksdb_compactionfilter_t310 unsafe extern "C" fn CFilterCreate(
311     arg: *mut c_void,
312     context: *mut rocksdb_compactionfiltercontext_t,
313 ) -> *mut rocksdb_compactionfilter_t {
314     rocksdb_compactionfilter_create(
315         ptr::null_mut(),
316         Some(CFilterDestroy),
317         Some(CFilterFilter),
318         Some(CFilterName),
319     )
320 }
321 
CheckCompaction( dbname: *const c_char, db: *mut rocksdb_t, options: *const rocksdb_options_t, roptions: *mut rocksdb_readoptions_t, woptions: *mut rocksdb_writeoptions_t, ) -> *mut rocksdb_t322 unsafe fn CheckCompaction(
323     dbname: *const c_char,
324     db: *mut rocksdb_t,
325     options: *const rocksdb_options_t,
326     roptions: *mut rocksdb_readoptions_t,
327     woptions: *mut rocksdb_writeoptions_t,
328 ) -> *mut rocksdb_t {
329     let mut err: *mut c_char = ptr::null_mut();
330     let db = rocksdb_open(options, dbname, &mut err);
331     CheckNoError!(err);
332     rocksdb_put(
333         db,
334         woptions,
335         cstrp!("foo"),
336         3,
337         cstrp!("foovalue"),
338         8,
339         &mut err,
340     );
341     CheckNoError!(err);
342     CheckGet(db, roptions, cstrp!("foo"), cstrp!("foovalue"));
343     rocksdb_put(
344         db,
345         woptions,
346         cstrp!("bar"),
347         3,
348         cstrp!("barvalue"),
349         8,
350         &mut err,
351     );
352     CheckNoError!(err);
353     CheckGet(db, roptions, cstrp!("bar"), cstrp!("barvalue"));
354     rocksdb_put(
355         db,
356         woptions,
357         cstrp!("baz"),
358         3,
359         cstrp!("bazvalue"),
360         8,
361         &mut err,
362     );
363     CheckNoError!(err);
364     CheckGet(db, roptions, cstrp!("baz"), cstrp!("bazvalue"));
365 
366     // Force compaction
367     rocksdb_compact_range(db, ptr::null(), 0, ptr::null(), 0);
368     // should have filtered bar, but not foo
369     CheckGet(db, roptions, cstrp!("foo"), cstrp!("foovalue"));
370     CheckGet(db, roptions, cstrp!("bar"), ptr::null());
371     CheckGet(db, roptions, cstrp!("baz"), cstrp!("newbazvalue"));
372     db
373 }
374 
375 // Custom merge operator
376 
MergeOperatorDestroy(arg: *mut c_void)377 unsafe extern "C" fn MergeOperatorDestroy(arg: *mut c_void) {}
378 
MergeOperatorName(arg: *mut c_void) -> *const c_char379 unsafe extern "C" fn MergeOperatorName(arg: *mut c_void) -> *const c_char {
380     cstrp!("foo")
381 }
382 
MergeOperatorFullMerge( arg: *mut c_void, key: *const c_char, key_length: size_t, existing_value: *const c_char, existing_value_length: size_t, operands_list: *const *const c_char, operands_list_length: *const size_t, num_operands: c_int, success: *mut u8, new_value_length: *mut size_t, ) -> *mut c_char383 unsafe extern "C" fn MergeOperatorFullMerge(
384     arg: *mut c_void,
385     key: *const c_char,
386     key_length: size_t,
387     existing_value: *const c_char,
388     existing_value_length: size_t,
389     operands_list: *const *const c_char,
390     operands_list_length: *const size_t,
391     num_operands: c_int,
392     success: *mut u8,
393     new_value_length: *mut size_t,
394 ) -> *mut c_char {
395     *new_value_length = 4;
396     *success = 1;
397     let result: *mut c_char = malloc(4) as *mut _;
398     memcpy(result as *mut _, cstrp!("fake") as *mut _, 4);
399     result
400 }
401 
MergeOperatorPartialMerge( arg: *mut c_void, key: *const c_char, key_length: size_t, operands_list: *const *const c_char, operands_list_length: *const size_t, num_operands: c_int, success: *mut u8, new_value_length: *mut size_t, ) -> *mut c_char402 unsafe extern "C" fn MergeOperatorPartialMerge(
403     arg: *mut c_void,
404     key: *const c_char,
405     key_length: size_t,
406     operands_list: *const *const c_char,
407     operands_list_length: *const size_t,
408     num_operands: c_int,
409     success: *mut u8,
410     new_value_length: *mut size_t,
411 ) -> *mut c_char {
412     *new_value_length = 4;
413     *success = 1;
414     let result: *mut c_char = malloc(4) as *mut _;
415     memcpy(result as *mut _, cstrp!("fake") as *const _, 4);
416     result
417 }
418 
419 #[test]
ffi()420 fn ffi() {
421     unsafe {
422         let mut db: *mut rocksdb_t;
423         let mut cmp: *mut rocksdb_comparator_t;
424         let mut cache: *mut rocksdb_cache_t;
425         let mut env: *mut rocksdb_env_t;
426         let mut options: *mut rocksdb_options_t;
427         let mut table_options: *mut rocksdb_block_based_table_options_t;
428         let mut roptions: *mut rocksdb_readoptions_t;
429         let mut woptions: *mut rocksdb_writeoptions_t;
430         let mut err: *mut c_char = ptr::null_mut();
431         let run: c_int = -1;
432 
433         let test_uuid = Uuid::new_v4().to_simple();
434 
435         let dbname = {
436             let mut dir = GetTempDir();
437             dir.push(format!("rocksdb_c_test-{}", test_uuid));
438             let path = dir.to_str().unwrap();
439             CString::new(path).unwrap()
440         };
441         let dbbackupname = {
442             let mut dir = GetTempDir();
443             dir.push(format!("rocksdb_c_test-{}-backup", test_uuid));
444             let path = dir.to_str().unwrap();
445             CString::new(path).unwrap()
446         };
447 
448         let dbname = dbname.as_ptr();
449         let dbbackupname = dbbackupname.as_ptr();
450 
451         StartPhase("create_objects");
452         cmp = rocksdb_comparator_create(
453             ptr::null_mut(),
454             Some(CmpDestroy),
455             Some(CmpCompare),
456             Some(CmpName),
457         );
458         env = rocksdb_create_default_env();
459         cache = rocksdb_cache_create_lru(100000);
460 
461         options = rocksdb_options_create();
462         rocksdb_options_set_comparator(options, cmp);
463         rocksdb_options_set_error_if_exists(options, 1);
464         rocksdb_options_set_env(options, env);
465         rocksdb_options_set_info_log(options, ptr::null_mut());
466         rocksdb_options_set_write_buffer_size(options, 100000);
467         rocksdb_options_set_paranoid_checks(options, 1);
468         rocksdb_options_set_max_open_files(options, 10);
469         table_options = rocksdb_block_based_options_create();
470         rocksdb_block_based_options_set_block_cache(table_options, cache);
471         rocksdb_options_set_block_based_table_factory(options, table_options);
472 
473         let no_compression = rocksdb_no_compression;
474         rocksdb_options_set_compression(options, no_compression as i32);
475         rocksdb_options_set_compression_options(options, -14, -1, 0, 0);
476         let compression_levels = vec![
477             no_compression,
478             no_compression,
479             no_compression,
480             no_compression,
481         ];
482         rocksdb_options_set_compression_per_level(
483             options,
484             mem::transmute(compression_levels.as_ptr()),
485             compression_levels.len() as size_t,
486         );
487 
488         roptions = rocksdb_readoptions_create();
489         rocksdb_readoptions_set_verify_checksums(roptions, 1);
490         rocksdb_readoptions_set_fill_cache(roptions, 0);
491 
492         woptions = rocksdb_writeoptions_create();
493         rocksdb_writeoptions_set_sync(woptions, 1);
494 
495         StartPhase("destroy");
496         rocksdb_destroy_db(options, dbname, &mut err);
497         Free(&mut err);
498 
499         StartPhase("open_error");
500         rocksdb_open(options, dbname, &mut err);
501         CheckCondition!(!err.is_null());
502         Free(&mut err);
503 
504         StartPhase("open");
505         rocksdb_options_set_create_if_missing(options, 1);
506         db = rocksdb_open(options, dbname, &mut err);
507         CheckNoError!(err);
508         CheckGet(db, roptions, cstrp!("foo") as *const _, ptr::null());
509 
510         StartPhase("put");
511         rocksdb_put(db, woptions, cstrp!("foo"), 3, cstrp!("hello"), 5, &mut err);
512         CheckNoError!(err);
513         CheckGet(db, roptions, cstrp!("foo"), cstrp!("hello"));
514 
515         StartPhase("backup_and_restore");
516         {
517             rocksdb_destroy_db(options, dbbackupname, &mut err);
518             CheckNoError!(err);
519 
520             let be = rocksdb_backup_engine_open(options, dbbackupname, &mut err);
521             CheckNoError!(err);
522 
523             rocksdb_backup_engine_create_new_backup(be, db, &mut err);
524             CheckNoError!(err);
525 
526             // need a change to trigger a new backup
527             rocksdb_delete(db, woptions, cstrp!("does-not-exist"), 14, &mut err);
528             CheckNoError!(err);
529 
530             rocksdb_backup_engine_create_new_backup(be, db, &mut err);
531             CheckNoError!(err);
532 
533             let bei: *const rocksdb_backup_engine_info_t =
534                 rocksdb_backup_engine_get_backup_info(be);
535             CheckCondition!(rocksdb_backup_engine_info_count(bei) > 1);
536             rocksdb_backup_engine_info_destroy(bei);
537 
538             rocksdb_backup_engine_purge_old_backups(be, 1, &mut err);
539             CheckNoError!(err);
540 
541             let bei: *const rocksdb_backup_engine_info_t =
542                 rocksdb_backup_engine_get_backup_info(be);
543             CheckCondition!(rocksdb_backup_engine_info_count(bei) == 1);
544             rocksdb_backup_engine_info_destroy(bei);
545 
546             rocksdb_delete(db, woptions, cstrp!("foo"), 3, &mut err);
547             CheckNoError!(err);
548 
549             rocksdb_close(db);
550 
551             rocksdb_destroy_db(options, dbname, &mut err);
552             CheckNoError!(err);
553 
554             let restore_options = rocksdb_restore_options_create();
555             rocksdb_restore_options_set_keep_log_files(restore_options, 0);
556             rocksdb_backup_engine_restore_db_from_latest_backup(
557                 be,
558                 dbname,
559                 dbname,
560                 restore_options,
561                 &mut err,
562             );
563             CheckNoError!(err);
564             rocksdb_restore_options_destroy(restore_options);
565 
566             rocksdb_options_set_error_if_exists(options, 0);
567             db = rocksdb_open(options, dbname, &mut err);
568             CheckNoError!(err);
569             rocksdb_options_set_error_if_exists(options, 1);
570 
571             CheckGet(db, roptions, cstrp!("foo"), cstrp!("hello"));
572 
573             rocksdb_backup_engine_close(be);
574         }
575 
576         StartPhase("compactall");
577         rocksdb_compact_range(db, ptr::null(), 0, ptr::null(), 0);
578         CheckGet(db, roptions, cstrp!("foo"), cstrp!("hello"));
579 
580         StartPhase("compactrange");
581         rocksdb_compact_range(db, cstrp!("a"), 1, cstrp!("z"), 1);
582         CheckGet(db, roptions, cstrp!("foo"), cstrp!("hello"));
583 
584         StartPhase("writebatch");
585         {
586             let mut wb = rocksdb_writebatch_create();
587             rocksdb_writebatch_put(wb, cstrp!("foo"), 3, cstrp!("a"), 1);
588             rocksdb_writebatch_clear(wb);
589             rocksdb_writebatch_put(wb, cstrp!("bar"), 3, cstrp!("b"), 1);
590             rocksdb_writebatch_put(wb, cstrp!("box"), 3, cstrp!("c"), 1);
591             rocksdb_writebatch_delete(wb, cstrp!("bar"), 3);
592             rocksdb_write(db, woptions, wb, &mut err);
593             CheckNoError!(err);
594             CheckGet(db, roptions, cstrp!("foo"), cstrp!("hello"));
595             CheckGet(db, roptions, cstrp!("bar"), ptr::null());
596             CheckGet(db, roptions, cstrp!("box"), cstrp!("c"));
597             let mut pos: c_int = 0;
598             rocksdb_writebatch_iterate(
599                 wb,
600                 mem::transmute(&mut pos),
601                 Some(CheckPut),
602                 Some(CheckDel),
603             );
604             CheckCondition!(pos == 3);
605             rocksdb_writebatch_destroy(wb);
606         }
607 
608         StartPhase("writebatch_vectors");
609         {
610             let wb = rocksdb_writebatch_create();
611             let k_list: [*const c_char; 2] = [cstrp!("z"), cstrp!("ap")];
612             let k_sizes: [size_t; 2] = [1, 2];
613             let v_list: [*const c_char; 3] = [cstrp!("x"), cstrp!("y"), cstrp!("z")];
614             let v_sizes: [size_t; 3] = [1, 1, 1];
615             rocksdb_writebatch_putv(
616                 wb,
617                 k_list.len() as c_int,
618                 k_list.as_ptr(),
619                 k_sizes.as_ptr(),
620                 v_list.len() as c_int,
621                 v_list.as_ptr(),
622                 v_sizes.as_ptr(),
623             );
624             rocksdb_write(db, woptions, wb, &mut err);
625             CheckNoError!(err);
626             CheckGet(db, roptions, cstrp!("zap"), cstrp!("xyz"));
627             rocksdb_writebatch_delete(wb, cstrp!("zap"), 3);
628             rocksdb_write(db, woptions, wb, &mut err);
629             CheckNoError!(err);
630             CheckGet(db, roptions, cstrp!("zap"), ptr::null());
631             rocksdb_writebatch_destroy(wb);
632         }
633 
634         StartPhase("writebatch_rep");
635         {
636             let wb1: *mut rocksdb_writebatch_t = rocksdb_writebatch_create();
637             rocksdb_writebatch_put(wb1, cstrp!("baz"), 3, cstrp!("d"), 1);
638             rocksdb_writebatch_put(wb1, cstrp!("quux"), 4, cstrp!("e"), 1);
639             rocksdb_writebatch_delete(wb1, cstrp!("quux"), 4);
640             let mut repsize1: size_t = 0;
641             let mut rep = rocksdb_writebatch_data(wb1, &mut repsize1) as *const c_void;
642             let mut wb2 = rocksdb_writebatch_create_from(rep as *const c_char, repsize1);
643             CheckCondition!(rocksdb_writebatch_count(wb1) == rocksdb_writebatch_count(wb2));
644             let mut repsize2: size_t = 0;
645             CheckCondition!(
646                 memcmp(
647                     rep,
648                     rocksdb_writebatch_data(wb2, &mut repsize2) as *const c_void,
649                     repsize1
650                 ) == 0
651             );
652             rocksdb_writebatch_destroy(wb1);
653             rocksdb_writebatch_destroy(wb2);
654         }
655 
656         StartPhase("iter");
657         {
658             let mut iter = rocksdb_create_iterator(db, roptions);
659             CheckCondition!(rocksdb_iter_valid(iter) == 0);
660             rocksdb_iter_seek_to_first(iter);
661             CheckCondition!(rocksdb_iter_valid(iter) != 0);
662             CheckIter(iter, cstrp!("box"), cstrp!("c"));
663             rocksdb_iter_next(iter);
664             CheckIter(iter, cstrp!("foo"), cstrp!("hello"));
665             rocksdb_iter_prev(iter);
666             CheckIter(iter, cstrp!("box"), cstrp!("c"));
667             rocksdb_iter_prev(iter);
668             CheckCondition!(rocksdb_iter_valid(iter) == 0);
669             rocksdb_iter_seek_to_last(iter);
670             CheckIter(iter, cstrp!("foo"), cstrp!("hello"));
671             rocksdb_iter_seek(iter, cstrp!("b"), 1);
672             CheckIter(iter, cstrp!("box"), cstrp!("c"));
673             rocksdb_iter_get_error(iter, &mut err);
674             CheckNoError!(err);
675             rocksdb_iter_destroy(iter);
676         }
677 
678         StartPhase("multiget");
679         {
680             let keys: [*const c_char; 3] = [cstrp!("box"), cstrp!("foo"), cstrp!("notfound")];
681             let keys_sizes: [size_t; 3] = [3, 3, 8];
682             let mut vals: [*mut c_char; 3] = [ptr::null_mut(), ptr::null_mut(), ptr::null_mut()];
683             let mut vals_sizes: [size_t; 3] = [0, 0, 0];
684             let mut errs: [*mut c_char; 3] = [ptr::null_mut(), ptr::null_mut(), ptr::null_mut()];
685             rocksdb_multi_get(
686                 db,
687                 roptions,
688                 3,
689                 keys.as_ptr(),
690                 keys_sizes.as_ptr(),
691                 vals.as_mut_ptr(),
692                 vals_sizes.as_mut_ptr(),
693                 errs.as_mut_ptr(),
694             );
695 
696             for i in 0..3 {
697                 CheckEqual(ptr::null(), errs[i], 0);
698                 match i {
699                     0 => CheckEqual(cstrp!("c"), vals[i], vals_sizes[i]),
700                     1 => CheckEqual(cstrp!("hello"), vals[i], vals_sizes[i]),
701                     2 => CheckEqual(ptr::null(), vals[i], vals_sizes[i]),
702                     _ => {}
703                 }
704                 Free(&mut vals[i]);
705             }
706         }
707 
708         StartPhase("approximate_sizes");
709         {
710             let mut sizes: [u64; 2] = [0, 0];
711             let start: [*const c_char; 2] = [cstrp!("a"), cstrp!("k00000000000000010000")];
712             let start_len: [size_t; 2] = [1, 21];
713             let limit: [*const c_char; 2] = [cstrp!("k00000000000000010000"), cstrp!("z")];
714             let limit_len: [size_t; 2] = [21, 1];
715             rocksdb_writeoptions_set_sync(woptions, 0);
716             for i in 0..20000 {
717                 let keybuf = CString::new(format!("k{:020}", i)).unwrap();
718                 let key = keybuf.to_bytes_with_nul();
719                 let valbuf = CString::new(format!("v{:020}", i)).unwrap();
720                 let val = valbuf.to_bytes_with_nul();
721                 rocksdb_put(
722                     db,
723                     woptions,
724                     key.as_ptr() as *const c_char,
725                     key.len() as size_t,
726                     val.as_ptr() as *const c_char,
727                     val.len() as size_t,
728                     &mut err,
729                 );
730                 CheckNoError!(err);
731             }
732             rocksdb_approximate_sizes(
733                 db,
734                 2,
735                 start.as_ptr(),
736                 start_len.as_ptr(),
737                 limit.as_ptr(),
738                 limit_len.as_ptr(),
739                 sizes.as_mut_ptr(),
740             );
741             CheckCondition!(sizes[0] > 0);
742             CheckCondition!(sizes[1] > 0);
743         }
744 
745         StartPhase("property");
746         {
747             let mut prop: *mut c_char;
748             prop = rocksdb_property_value(db, cstrp!("nosuchprop"));
749             CheckCondition!(prop.is_null());
750             prop = rocksdb_property_value(db, cstrp!("rocksdb.stats"));
751             CheckCondition!(!prop.is_null());
752             Free(&mut prop);
753         }
754 
755         StartPhase("snapshot");
756         {
757             let snap: *const rocksdb_snapshot_t = rocksdb_create_snapshot(db);
758             rocksdb_delete(db, woptions, cstrp!("foo"), 3, &mut err);
759             CheckNoError!(err);
760             rocksdb_readoptions_set_snapshot(roptions, snap);
761             CheckGet(db, roptions, cstrp!("foo"), cstrp!("hello"));
762             rocksdb_readoptions_set_snapshot(roptions, ptr::null());
763             CheckGet(db, roptions, cstrp!("foo"), ptr::null());
764             rocksdb_release_snapshot(db, snap);
765         }
766 
767         StartPhase("repair");
768         {
769             // If we do not compact here, then the lazy deletion of files (https://reviews.facebook.net/D6123) would leave around deleted files and the repair process will find those files and put them back into the database.
770             rocksdb_compact_range(db, ptr::null(), 0, ptr::null(), 0);
771             rocksdb_close(db);
772             rocksdb_options_set_create_if_missing(options, 0);
773             rocksdb_options_set_error_if_exists(options, 0);
774             // rocksdb_options_set_wal_recovery_mode(options, 2);
775             rocksdb_repair_db(options, dbname, &mut err);
776             CheckNoError!(err);
777             db = rocksdb_open(options, dbname, &mut err);
778             CheckNoError!(err);
779             CheckGet(db, roptions, cstrp!("foo"), ptr::null());
780             CheckGet(db, roptions, cstrp!("bar"), ptr::null());
781             CheckGet(db, roptions, cstrp!("box"), cstrp!("c"));
782             rocksdb_options_set_create_if_missing(options, 1);
783             rocksdb_options_set_error_if_exists(options, 1);
784         }
785 
786         StartPhase("filter");
787         for run in 0..2 {
788             // First run uses custom filter, second run uses bloom filter
789             CheckNoError!(err);
790             let mut policy: *mut rocksdb_filterpolicy_t = if run == 0 {
791                 rocksdb_filterpolicy_create(
792                     ptr::null_mut(),
793                     Some(FilterDestroy),
794                     Some(FilterCreate),
795                     Some(FilterKeyMatch),
796                     None,
797                     Some(FilterName),
798                 )
799             } else {
800                 rocksdb_filterpolicy_create_bloom(10)
801             };
802 
803             rocksdb_block_based_options_set_filter_policy(table_options, policy);
804 
805             // Create new database
806             rocksdb_close(db);
807             rocksdb_destroy_db(options, dbname, &mut err);
808             rocksdb_options_set_block_based_table_factory(options, table_options);
809             db = rocksdb_open(options, dbname, &mut err);
810             CheckNoError!(err);
811             rocksdb_put(
812                 db,
813                 woptions,
814                 cstrp!("foo"),
815                 3,
816                 cstrp!("foovalue"),
817                 8,
818                 &mut err,
819             );
820             CheckNoError!(err);
821             rocksdb_put(
822                 db,
823                 woptions,
824                 cstrp!("bar"),
825                 3,
826                 cstrp!("barvalue"),
827                 8,
828                 &mut err,
829             );
830             CheckNoError!(err);
831             rocksdb_compact_range(db, ptr::null(), 0, ptr::null(), 0);
832 
833             fake_filter_result = 1;
834             CheckGet(db, roptions, cstrp!("foo"), cstrp!("foovalue"));
835             CheckGet(db, roptions, cstrp!("bar"), cstrp!("barvalue"));
836             if phase == "" {
837                 // Must not find value when custom filter returns false
838                 fake_filter_result = 0;
839                 CheckGet(db, roptions, cstrp!("foo"), ptr::null());
840                 CheckGet(db, roptions, cstrp!("bar"), ptr::null());
841                 fake_filter_result = 1;
842 
843                 CheckGet(db, roptions, cstrp!("foo"), cstrp!("foovalue"));
844                 CheckGet(db, roptions, cstrp!("bar"), cstrp!("barvalue"));
845             }
846             // Reset the policy
847             rocksdb_block_based_options_set_filter_policy(table_options, ptr::null_mut());
848             rocksdb_options_set_block_based_table_factory(options, table_options);
849         }
850 
851         StartPhase("compaction_filter");
852         {
853             let options_with_filter = rocksdb_options_create();
854             rocksdb_options_set_create_if_missing(options_with_filter, 1);
855             let cfilter = rocksdb_compactionfilter_create(
856                 ptr::null_mut(),
857                 Some(CFilterDestroy),
858                 Some(CFilterFilter),
859                 Some(CFilterName),
860             );
861             // Create new database
862             rocksdb_close(db);
863             rocksdb_destroy_db(options_with_filter, dbname, &mut err);
864             rocksdb_options_set_compaction_filter(options_with_filter, cfilter);
865             db = CheckCompaction(dbname, db, options_with_filter, roptions, woptions);
866 
867             rocksdb_options_set_compaction_filter(options_with_filter, ptr::null_mut());
868             rocksdb_compactionfilter_destroy(cfilter);
869             rocksdb_options_destroy(options_with_filter);
870         }
871 
872         StartPhase("compaction_filter_factory");
873         {
874             let mut options_with_filter_factory = rocksdb_options_create();
875             rocksdb_options_set_create_if_missing(options_with_filter_factory, 1);
876             let mut factory = rocksdb_compactionfilterfactory_create(
877                 ptr::null_mut(),
878                 Some(CFilterFactoryDestroy),
879                 Some(CFilterCreate),
880                 Some(CFilterFactoryName),
881             );
882             // Create new database
883             rocksdb_close(db);
884             rocksdb_destroy_db(options_with_filter_factory, dbname, &mut err);
885             rocksdb_options_set_compaction_filter_factory(options_with_filter_factory, factory);
886             db = CheckCompaction(dbname, db, options_with_filter_factory, roptions, woptions);
887 
888             rocksdb_options_set_compaction_filter_factory(
889                 options_with_filter_factory,
890                 ptr::null_mut(),
891             );
892             rocksdb_options_destroy(options_with_filter_factory);
893         }
894 
895         StartPhase("merge_operator");
896         {
897             let mut merge_operator = rocksdb_mergeoperator_create(
898                 ptr::null_mut(),
899                 Some(MergeOperatorDestroy),
900                 Some(MergeOperatorFullMerge),
901                 Some(MergeOperatorPartialMerge),
902                 None,
903                 Some(MergeOperatorName),
904             );
905             // Create new database
906             rocksdb_close(db);
907             rocksdb_destroy_db(options, dbname, &mut err);
908             rocksdb_options_set_merge_operator(options, merge_operator);
909             db = rocksdb_open(options, dbname, &mut err);
910             CheckNoError!(err);
911             rocksdb_put(
912                 db,
913                 woptions,
914                 cstrp!("foo"),
915                 3,
916                 cstrp!("foovalue"),
917                 8,
918                 &mut err,
919             );
920             CheckNoError!(err);
921             CheckGet(db, roptions, cstrp!("foo"), cstrp!("foovalue"));
922             rocksdb_merge(
923                 db,
924                 woptions,
925                 cstrp!("foo"),
926                 3,
927                 cstrp!("barvalue"),
928                 8,
929                 &mut err,
930             );
931             CheckNoError!(err);
932             CheckGet(db, roptions, cstrp!("foo"), cstrp!("fake"));
933 
934             // Merge of a non-existing value
935             rocksdb_merge(
936                 db,
937                 woptions,
938                 cstrp!("bar"),
939                 3,
940                 cstrp!("barvalue"),
941                 8,
942                 &mut err,
943             );
944             CheckNoError!(err);
945             CheckGet(db, roptions, cstrp!("bar"), cstrp!("fake"));
946         }
947 
948         StartPhase("columnfamilies");
949         {
950             rocksdb_close(db);
951             rocksdb_destroy_db(options, dbname, &mut err);
952             CheckNoError!(err);
953 
954             let mut db_options = rocksdb_options_create();
955             rocksdb_options_set_create_if_missing(db_options, 1);
956             db = rocksdb_open(db_options, dbname, &mut err);
957             CheckNoError!(err);
958             let mut cfh = rocksdb_create_column_family(db, db_options, cstrp!("cf1"), &mut err);
959             rocksdb_column_family_handle_destroy(cfh);
960             CheckNoError!(err);
961             rocksdb_close(db);
962 
963             let mut cflen: size_t = 0;
964             let column_fams_raw =
965                 rocksdb_list_column_families(db_options, dbname, &mut cflen, &mut err);
966             let column_fams = slice::from_raw_parts(column_fams_raw, cflen as usize);
967             CheckEqual(cstrp!("default"), column_fams[0], 7);
968             CheckEqual(cstrp!("cf1"), column_fams[1], 3);
969             CheckCondition!(cflen == 2);
970             rocksdb_list_column_families_destroy(column_fams_raw, cflen);
971 
972             let mut cf_options = rocksdb_options_create();
973 
974             let mut cf_names: [*const c_char; 2] = [cstrp!("default"), cstrp!("cf1")];
975             let mut cf_opts: [*const rocksdb_options_t; 2] = [cf_options, cf_options];
976             let mut handles: [*mut rocksdb_column_family_handle_t; 2] =
977                 [ptr::null_mut(), ptr::null_mut()];
978             db = rocksdb_open_column_families(
979                 db_options,
980                 dbname,
981                 2,
982                 cf_names.as_mut_ptr(),
983                 cf_opts.as_mut_ptr(),
984                 handles.as_mut_ptr(),
985                 &mut err,
986             );
987             CheckNoError!(err);
988 
989             rocksdb_put_cf(
990                 db,
991                 woptions,
992                 handles[1],
993                 cstrp!("foo"),
994                 3,
995                 cstrp!("hello"),
996                 5,
997                 &mut err,
998             );
999             CheckNoError!(err);
1000 
1001             CheckGetCF(db, roptions, handles[1], cstrp!("foo"), cstrp!("hello"));
1002 
1003             rocksdb_delete_cf(db, woptions, handles[1], cstrp!("foo"), 3, &mut err);
1004             CheckNoError!(err);
1005 
1006             CheckGetCF(db, roptions, handles[1], cstrp!("foo"), ptr::null());
1007 
1008             let mut wb = rocksdb_writebatch_create();
1009             rocksdb_writebatch_put_cf(wb, handles[1], cstrp!("baz"), 3, cstrp!("a"), 1);
1010             rocksdb_writebatch_clear(wb);
1011             rocksdb_writebatch_put_cf(wb, handles[1], cstrp!("bar"), 3, cstrp!("b"), 1);
1012             rocksdb_writebatch_put_cf(wb, handles[1], cstrp!("box"), 3, cstrp!("c"), 1);
1013             rocksdb_writebatch_delete_cf(wb, handles[1], cstrp!("bar"), 3);
1014             rocksdb_write(db, woptions, wb, &mut err);
1015             CheckNoError!(err);
1016             CheckGetCF(db, roptions, handles[1], cstrp!("baz"), ptr::null());
1017             CheckGetCF(db, roptions, handles[1], cstrp!("bar"), ptr::null());
1018             CheckGetCF(db, roptions, handles[1], cstrp!("box"), cstrp!("c"));
1019             rocksdb_writebatch_destroy(wb);
1020 
1021             let keys: [*const c_char; 3] = [cstrp!("box"), cstrp!("box"), cstrp!("barfooxx")];
1022             let get_handles: [*const rocksdb_column_family_handle_t; 3] =
1023                 [handles[0], handles[1], handles[1]];
1024             let keys_sizes: [size_t; 3] = [3, 3, 8];
1025             let mut vals: [*mut c_char; 3] = [ptr::null_mut(), ptr::null_mut(), ptr::null_mut()];
1026             let mut vals_sizes: [size_t; 3] = [0, 0, 0];
1027             let mut errs: [*mut c_char; 3] = [ptr::null_mut(), ptr::null_mut(), ptr::null_mut()];
1028             rocksdb_multi_get_cf(
1029                 db,
1030                 roptions,
1031                 get_handles.as_ptr(),
1032                 3,
1033                 keys.as_ptr(),
1034                 keys_sizes.as_ptr(),
1035                 vals.as_mut_ptr(),
1036                 vals_sizes.as_mut_ptr(),
1037                 errs.as_mut_ptr(),
1038             );
1039 
1040             for i in 0..3 {
1041                 CheckEqual(ptr::null(), errs[i], 0);
1042                 match i {
1043                     0 => CheckEqual(ptr::null(), vals[i], vals_sizes[i]), // wrong cf
1044                     1 => CheckEqual(cstrp!("c"), vals[i], vals_sizes[i]), // bingo
1045                     2 => CheckEqual(ptr::null(), vals[i], vals_sizes[i]), // normal not found
1046                     _ => {}
1047                 }
1048                 Free(&mut vals[i]);
1049             }
1050 
1051             let mut iter = rocksdb_create_iterator_cf(db, roptions, handles[1]);
1052             CheckCondition!(rocksdb_iter_valid(iter) == 0);
1053             rocksdb_iter_seek_to_first(iter);
1054             CheckCondition!(rocksdb_iter_valid(iter) != 0);
1055 
1056             let mut i: u32 = 0;
1057             while rocksdb_iter_valid(iter) != 0 {
1058                 rocksdb_iter_next(iter);
1059                 i += 1;
1060             }
1061             CheckCondition!(i == 1);
1062             rocksdb_iter_get_error(iter, &mut err);
1063             CheckNoError!(err);
1064             rocksdb_iter_destroy(iter);
1065 
1066             let mut iters_cf_handles: [*mut rocksdb_column_family_handle_t; 2] =
1067                 [handles[0], handles[1]];
1068             let mut iters_handles: [*mut rocksdb_iterator_t; 2] =
1069                 [ptr::null_mut(), ptr::null_mut()];
1070             rocksdb_create_iterators(
1071                 db,
1072                 roptions,
1073                 iters_cf_handles.as_mut_ptr(),
1074                 iters_handles.as_mut_ptr(),
1075                 2,
1076                 &mut err,
1077             );
1078             CheckNoError!(err);
1079 
1080             iter = iters_handles[0];
1081             CheckCondition!(rocksdb_iter_valid(iter) == 0);
1082             rocksdb_iter_seek_to_first(iter);
1083             CheckCondition!(rocksdb_iter_valid(iter) == 0);
1084             rocksdb_iter_destroy(iter);
1085 
1086             iter = iters_handles[1];
1087             CheckCondition!(rocksdb_iter_valid(iter) == 0);
1088             rocksdb_iter_seek_to_first(iter);
1089             CheckCondition!(rocksdb_iter_valid(iter) != 0);
1090 
1091             let mut i: u32 = 0;
1092             while rocksdb_iter_valid(iter) != 0 {
1093                 rocksdb_iter_next(iter);
1094                 i += 1;
1095             }
1096             CheckCondition!(i == 1);
1097             rocksdb_iter_get_error(iter, &mut err);
1098             CheckNoError!(err);
1099             rocksdb_iter_destroy(iter);
1100 
1101             rocksdb_drop_column_family(db, handles[1], &mut err);
1102             CheckNoError!(err);
1103             for i in 0..2 {
1104                 rocksdb_column_family_handle_destroy(handles[i]);
1105             }
1106             rocksdb_close(db);
1107             rocksdb_destroy_db(options, dbname, &mut err);
1108             rocksdb_options_destroy(db_options);
1109             rocksdb_options_destroy(cf_options);
1110         }
1111 
1112         StartPhase("prefix");
1113         {
1114             // Create new database
1115             rocksdb_options_set_allow_mmap_reads(options, 1);
1116             rocksdb_options_set_prefix_extractor(
1117                 options,
1118                 rocksdb_slicetransform_create_fixed_prefix(3),
1119             );
1120             rocksdb_options_set_hash_skip_list_rep(options, 5000, 4, 4);
1121             rocksdb_options_set_plain_table_factory(options, 4, 10, 0.75, 16);
1122             rocksdb_options_set_allow_concurrent_memtable_write(options, 0);
1123 
1124             db = rocksdb_open(options, dbname, &mut err);
1125             CheckNoError!(err);
1126 
1127             rocksdb_put(db, woptions, cstrp!("foo1"), 4, cstrp!("foo"), 3, &mut err);
1128             CheckNoError!(err);
1129             rocksdb_put(db, woptions, cstrp!("foo2"), 4, cstrp!("foo"), 3, &mut err);
1130             CheckNoError!(err);
1131             rocksdb_put(db, woptions, cstrp!("foo3"), 4, cstrp!("foo"), 3, &mut err);
1132             CheckNoError!(err);
1133             rocksdb_put(db, woptions, cstrp!("bar1"), 4, cstrp!("bar"), 3, &mut err);
1134             CheckNoError!(err);
1135             rocksdb_put(db, woptions, cstrp!("bar2"), 4, cstrp!("bar"), 3, &mut err);
1136             CheckNoError!(err);
1137             rocksdb_put(db, woptions, cstrp!("bar3"), 4, cstrp!("bar"), 3, &mut err);
1138             CheckNoError!(err);
1139 
1140             let mut iter = rocksdb_create_iterator(db, roptions);
1141             CheckCondition!(rocksdb_iter_valid(iter) == 0);
1142 
1143             rocksdb_iter_seek(iter, cstrp!("bar"), 3);
1144             rocksdb_iter_get_error(iter, &mut err);
1145             CheckNoError!(err);
1146             CheckCondition!(rocksdb_iter_valid(iter) != 0);
1147 
1148             CheckIter(iter, cstrp!("bar1"), cstrp!("bar"));
1149             rocksdb_iter_next(iter);
1150             CheckIter(iter, cstrp!("bar2"), cstrp!("bar"));
1151             rocksdb_iter_next(iter);
1152             CheckIter(iter, cstrp!("bar3"), cstrp!("bar"));
1153             rocksdb_iter_get_error(iter, &mut err);
1154             CheckNoError!(err);
1155             rocksdb_iter_destroy(iter);
1156 
1157             rocksdb_close(db);
1158             rocksdb_destroy_db(options, dbname, &mut err);
1159         }
1160 
1161         StartPhase("cuckoo_options");
1162         {
1163             let mut cuckoo_options = rocksdb_cuckoo_options_create();
1164             rocksdb_cuckoo_options_set_hash_ratio(cuckoo_options, 0.5);
1165             rocksdb_cuckoo_options_set_max_search_depth(cuckoo_options, 200);
1166             rocksdb_cuckoo_options_set_cuckoo_block_size(cuckoo_options, 10);
1167             rocksdb_cuckoo_options_set_identity_as_first_hash(cuckoo_options, 1);
1168             rocksdb_cuckoo_options_set_use_module_hash(cuckoo_options, 0);
1169             rocksdb_options_set_cuckoo_table_factory(options, cuckoo_options);
1170 
1171             db = rocksdb_open(options, dbname, &mut err);
1172             CheckNoError!(err);
1173 
1174             rocksdb_cuckoo_options_destroy(cuckoo_options);
1175         }
1176 
1177         StartPhase("iterate_upper_bound");
1178         {
1179             // Create new empty database
1180             rocksdb_close(db);
1181             rocksdb_destroy_db(options, dbname, &mut err);
1182             CheckNoError!(err);
1183 
1184             rocksdb_options_set_prefix_extractor(options, ptr::null_mut());
1185             db = rocksdb_open(options, dbname, &mut err);
1186             CheckNoError!(err);
1187 
1188             rocksdb_put(db, woptions, cstrp!("a"), 1, cstrp!("0"), 1, &mut err);
1189             CheckNoError!(err);
1190             rocksdb_put(db, woptions, cstrp!("foo"), 3, cstrp!("bar"), 3, &mut err);
1191             CheckNoError!(err);
1192             rocksdb_put(db, woptions, cstrp!("foo1"), 4, cstrp!("bar1"), 4, &mut err);
1193             CheckNoError!(err);
1194             rocksdb_put(db, woptions, cstrp!("g1"), 2, cstrp!("0"), 1, &mut err);
1195             CheckNoError!(err);
1196 
1197             // testing basic case with no iterate_upper_bound and no prefix_extractor
1198             {
1199                 rocksdb_readoptions_set_iterate_upper_bound(roptions, ptr::null(), 0);
1200                 let mut iter = rocksdb_create_iterator(db, roptions);
1201 
1202                 rocksdb_iter_seek(iter, cstrp!("foo"), 3);
1203                 CheckCondition!(rocksdb_iter_valid(iter) != 0);
1204                 CheckIter(iter, cstrp!("foo"), cstrp!("bar"));
1205 
1206                 rocksdb_iter_next(iter);
1207                 CheckCondition!(rocksdb_iter_valid(iter) != 0);
1208                 CheckIter(iter, cstrp!("foo1"), cstrp!("bar1"));
1209 
1210                 rocksdb_iter_next(iter);
1211                 CheckCondition!(rocksdb_iter_valid(iter) != 0);
1212                 CheckIter(iter, cstrp!("g1"), cstrp!("0"));
1213 
1214                 rocksdb_iter_destroy(iter);
1215             }
1216 
1217             // testing iterate_upper_bound and forward iterator
1218             // to make sure it stops at bound
1219             {
1220                 // iterate_upper_bound points beyond the last expected entry
1221                 rocksdb_readoptions_set_iterate_upper_bound(roptions, cstrp!("foo2"), 4);
1222 
1223                 let mut iter = rocksdb_create_iterator(db, roptions);
1224 
1225                 rocksdb_iter_seek(iter, cstrp!("foo"), 3);
1226                 CheckCondition!(rocksdb_iter_valid(iter) != 0);
1227                 CheckIter(iter, cstrp!("foo"), cstrp!("bar"));
1228 
1229                 rocksdb_iter_next(iter);
1230                 CheckCondition!(rocksdb_iter_valid(iter) != 0);
1231                 CheckIter(iter, cstrp!("foo1"), cstrp!("bar1"));
1232 
1233                 rocksdb_iter_next(iter);
1234                 // should stop here...
1235                 CheckCondition!(rocksdb_iter_valid(iter) == 0);
1236 
1237                 rocksdb_iter_destroy(iter);
1238             }
1239         }
1240 
1241         // Simple sanity check that setting memtable rep works.
1242         StartPhase("memtable_reps");
1243         {
1244             // Create database with vector memtable.
1245             rocksdb_close(db);
1246             rocksdb_destroy_db(options, dbname, &mut err);
1247             CheckNoError!(err);
1248 
1249             rocksdb_options_set_memtable_vector_rep(options);
1250             db = rocksdb_open(options, dbname, &mut err);
1251             CheckNoError!(err);
1252 
1253             // // Create database with hash skiplist memtable.
1254             // rocksdb_close(db);
1255             // rocksdb_destroy_db(options, dbname, &mut err);
1256             // CheckNoError!(err);
1257             //
1258             // rocksdb_options_set_hash_skip_list_rep(options, 5000, 4, 4);
1259             // db = rocksdb_open(options, dbname, &mut err);
1260             // CheckNoError!(err);
1261         }
1262 
1263         StartPhase("cleanup");
1264         rocksdb_close(db);
1265         rocksdb_options_destroy(options);
1266         rocksdb_block_based_options_destroy(table_options);
1267         rocksdb_readoptions_destroy(roptions);
1268         rocksdb_writeoptions_destroy(woptions);
1269         rocksdb_cache_destroy(cache);
1270         rocksdb_comparator_destroy(cmp);
1271         rocksdb_env_destroy(env);
1272 
1273         err_println!("PASS");
1274     }
1275 }
1276