1 /* Copyright (c) 2011 The LevelDB Authors. All rights reserved.
2    Use of this source code is governed by a BSD-style license that can be
3    found in the LICENSE file. See the AUTHORS file for names of contributors. */
4 // Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
5 
6 #include <stdio.h>
7 
8 #ifndef ROCKSDB_LITE  // Lite does not support C API
9 
10 #include "rocksdb/c.h"
11 
12 #include <stddef.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <sys/types.h>
16 #ifndef OS_WIN
17 #include <unistd.h>
18 #endif
19 #include <inttypes.h>
20 
21 // Can not use port/port.h macros as this is a c file
22 #ifdef OS_WIN
23 #include <windows.h>
24 
25 // Ok for uniqueness
geteuid()26 int geteuid() {
27   int result = 0;
28 
29   result = ((int)GetCurrentProcessId() << 16);
30   result |= (int)GetCurrentThreadId();
31 
32   return result;
33 }
34 
35 // VS < 2015
36 #if defined(_MSC_VER) && (_MSC_VER < 1900)
37 #define snprintf _snprintf
38 #endif
39 
40 #endif
41 
42 const char* phase = "";
43 static char dbname[200];
44 static char sstfilename[200];
45 static char dbbackupname[200];
46 static char dbcheckpointname[200];
47 static char dbpathname[200];
48 static char secondary_path[200];
49 
StartPhase(const char * name)50 static void StartPhase(const char* name) {
51   fprintf(stderr, "=== Test %s\n", name);
52   phase = name;
53 }
54 #ifdef _MSC_VER
55 #pragma warning(push)
56 #pragma warning (disable: 4996) // getenv security warning
57 #endif
GetTempDir(void)58 static const char* GetTempDir(void) {
59     const char* ret = getenv("TEST_TMPDIR");
60     if (ret == NULL || ret[0] == '\0')
61         ret = "/tmp";
62     return ret;
63 }
64 #ifdef _MSC_VER
65 #pragma warning(pop)
66 #endif
67 
68 #define CheckNoError(err)                                               \
69   if ((err) != NULL) {                                                  \
70     fprintf(stderr, "%s:%d: %s: %s\n", __FILE__, __LINE__, phase, (err)); \
71     abort();                                                            \
72   }
73 
74 #define CheckCondition(cond)                                            \
75   if (!(cond)) {                                                        \
76     fprintf(stderr, "%s:%d: %s: %s\n", __FILE__, __LINE__, phase, #cond); \
77     abort();                                                            \
78   }
79 
CheckEqual(const char * expected,const char * v,size_t n)80 static void CheckEqual(const char* expected, const char* v, size_t n) {
81   if (expected == NULL && v == NULL) {
82     // ok
83   } else if (expected != NULL && v != NULL && n == strlen(expected) &&
84              memcmp(expected, v, n) == 0) {
85     // ok
86     return;
87   } else {
88     fprintf(stderr, "%s: expected '%s', got '%s'\n",
89             phase,
90             (expected ? expected : "(null)"),
91             (v ? v : "(null"));
92     abort();
93   }
94 }
95 
Free(char ** ptr)96 static void Free(char** ptr) {
97   if (*ptr) {
98     free(*ptr);
99     *ptr = NULL;
100   }
101 }
102 
CheckValue(char * err,const char * expected,char ** actual,size_t actual_length)103 static void CheckValue(
104     char* err,
105     const char* expected,
106     char** actual,
107     size_t actual_length) {
108   CheckNoError(err);
109   CheckEqual(expected, *actual, actual_length);
110   Free(actual);
111 }
112 
CheckGet(rocksdb_t * db,const rocksdb_readoptions_t * options,const char * key,const char * expected)113 static void CheckGet(
114     rocksdb_t* db,
115     const rocksdb_readoptions_t* options,
116     const char* key,
117     const char* expected) {
118   char* err = NULL;
119   size_t val_len;
120   char* val;
121   val = rocksdb_get(db, options, key, strlen(key), &val_len, &err);
122   CheckNoError(err);
123   CheckEqual(expected, val, val_len);
124   Free(&val);
125 }
126 
CheckGetCF(rocksdb_t * db,const rocksdb_readoptions_t * options,rocksdb_column_family_handle_t * handle,const char * key,const char * expected)127 static void CheckGetCF(
128     rocksdb_t* db,
129     const rocksdb_readoptions_t* options,
130     rocksdb_column_family_handle_t* handle,
131     const char* key,
132     const char* expected) {
133   char* err = NULL;
134   size_t val_len;
135   char* val;
136   val = rocksdb_get_cf(db, options, handle, key, strlen(key), &val_len, &err);
137   CheckNoError(err);
138   CheckEqual(expected, val, val_len);
139   Free(&val);
140 }
141 
CheckPinGet(rocksdb_t * db,const rocksdb_readoptions_t * options,const char * key,const char * expected)142 static void CheckPinGet(rocksdb_t* db, const rocksdb_readoptions_t* options,
143                         const char* key, const char* expected) {
144   char* err = NULL;
145   size_t val_len;
146   const char* val;
147   rocksdb_pinnableslice_t* p;
148   p = rocksdb_get_pinned(db, options, key, strlen(key), &err);
149   CheckNoError(err);
150   val = rocksdb_pinnableslice_value(p, &val_len);
151   CheckEqual(expected, val, val_len);
152   rocksdb_pinnableslice_destroy(p);
153 }
154 
CheckPinGetCF(rocksdb_t * db,const rocksdb_readoptions_t * options,rocksdb_column_family_handle_t * handle,const char * key,const char * expected)155 static void CheckPinGetCF(rocksdb_t* db, const rocksdb_readoptions_t* options,
156                           rocksdb_column_family_handle_t* handle,
157                           const char* key, const char* expected) {
158   char* err = NULL;
159   size_t val_len;
160   const char* val;
161   rocksdb_pinnableslice_t* p;
162   p = rocksdb_get_pinned_cf(db, options, handle, key, strlen(key), &err);
163   CheckNoError(err);
164   val = rocksdb_pinnableslice_value(p, &val_len);
165   CheckEqual(expected, val, val_len);
166   rocksdb_pinnableslice_destroy(p);
167 }
168 
CheckIter(rocksdb_iterator_t * iter,const char * key,const char * val)169 static void CheckIter(rocksdb_iterator_t* iter,
170                       const char* key, const char* val) {
171   size_t len;
172   const char* str;
173   str = rocksdb_iter_key(iter, &len);
174   CheckEqual(key, str, len);
175   str = rocksdb_iter_value(iter, &len);
176   CheckEqual(val, str, len);
177 }
178 
179 // Callback from rocksdb_writebatch_iterate()
CheckPut(void * ptr,const char * k,size_t klen,const char * v,size_t vlen)180 static void CheckPut(void* ptr,
181                      const char* k, size_t klen,
182                      const char* v, size_t vlen) {
183   int* state = (int*) ptr;
184   CheckCondition(*state < 2);
185   switch (*state) {
186     case 0:
187       CheckEqual("bar", k, klen);
188       CheckEqual("b", v, vlen);
189       break;
190     case 1:
191       CheckEqual("box", k, klen);
192       CheckEqual("c", v, vlen);
193       break;
194   }
195   (*state)++;
196 }
197 
198 // Callback from rocksdb_writebatch_iterate()
CheckDel(void * ptr,const char * k,size_t klen)199 static void CheckDel(void* ptr, const char* k, size_t klen) {
200   int* state = (int*) ptr;
201   CheckCondition(*state == 2);
202   CheckEqual("bar", k, klen);
203   (*state)++;
204 }
205 
CmpDestroy(void * arg)206 static void CmpDestroy(void* arg) { (void)arg; }
207 
CmpCompare(void * arg,const char * a,size_t alen,const char * b,size_t blen)208 static int CmpCompare(void* arg, const char* a, size_t alen,
209                       const char* b, size_t blen) {
210   (void)arg;
211   size_t n = (alen < blen) ? alen : blen;
212   int r = memcmp(a, b, n);
213   if (r == 0) {
214     if (alen < blen) r = -1;
215     else if (alen > blen) r = +1;
216   }
217   return r;
218 }
219 
CmpName(void * arg)220 static const char* CmpName(void* arg) {
221   (void)arg;
222   return "foo";
223 }
224 
225 // Custom filter policy
226 static unsigned char fake_filter_result = 1;
FilterDestroy(void * arg)227 static void FilterDestroy(void* arg) { (void)arg; }
FilterName(void * arg)228 static const char* FilterName(void* arg) {
229   (void)arg;
230   return "TestFilter";
231 }
FilterCreate(void * arg,const char * const * key_array,const size_t * key_length_array,int num_keys,size_t * filter_length)232 static char* FilterCreate(
233     void* arg,
234     const char* const* key_array, const size_t* key_length_array,
235     int num_keys,
236     size_t* filter_length) {
237   (void)arg;
238   (void)key_array;
239   (void)key_length_array;
240   (void)num_keys;
241   *filter_length = 4;
242   char* result = malloc(4);
243   memcpy(result, "fake", 4);
244   return result;
245 }
FilterKeyMatch(void * arg,const char * key,size_t length,const char * filter,size_t filter_length)246 static unsigned char FilterKeyMatch(
247     void* arg,
248     const char* key, size_t length,
249     const char* filter, size_t filter_length) {
250   (void)arg;
251   (void)key;
252   (void)length;
253   CheckCondition(filter_length == 4);
254   CheckCondition(memcmp(filter, "fake", 4) == 0);
255   return fake_filter_result;
256 }
257 
258 // Custom compaction filter
CFilterDestroy(void * arg)259 static void CFilterDestroy(void* arg) { (void)arg; }
CFilterName(void * arg)260 static const char* CFilterName(void* arg) {
261   (void)arg;
262   return "foo";
263 }
CFilterFilter(void * arg,int level,const char * key,size_t key_length,const char * existing_value,size_t value_length,char ** new_value,size_t * new_value_length,unsigned char * value_changed)264 static unsigned char CFilterFilter(void* arg, int level, const char* key,
265                                    size_t key_length,
266                                    const char* existing_value,
267                                    size_t value_length, char** new_value,
268                                    size_t* new_value_length,
269                                    unsigned char* value_changed) {
270   (void)arg;
271   (void)level;
272   (void)existing_value;
273   (void)value_length;
274   if (key_length == 3) {
275     if (memcmp(key, "bar", key_length) == 0) {
276       return 1;
277     } else if (memcmp(key, "baz", key_length) == 0) {
278       *value_changed = 1;
279       *new_value = "newbazvalue";
280       *new_value_length = 11;
281       return 0;
282     }
283   }
284   return 0;
285 }
286 
CFilterFactoryDestroy(void * arg)287 static void CFilterFactoryDestroy(void* arg) { (void)arg; }
CFilterFactoryName(void * arg)288 static const char* CFilterFactoryName(void* arg) {
289   (void)arg;
290   return "foo";
291 }
CFilterCreate(void * arg,rocksdb_compactionfiltercontext_t * context)292 static rocksdb_compactionfilter_t* CFilterCreate(
293     void* arg, rocksdb_compactionfiltercontext_t* context) {
294   (void)arg;
295   (void)context;
296   return rocksdb_compactionfilter_create(NULL, CFilterDestroy, CFilterFilter,
297                                          CFilterName);
298 }
299 
CheckCompaction(rocksdb_t * db,rocksdb_options_t * options,rocksdb_readoptions_t * roptions,rocksdb_writeoptions_t * woptions)300 static rocksdb_t* CheckCompaction(rocksdb_t* db, rocksdb_options_t* options,
301                                   rocksdb_readoptions_t* roptions,
302                                   rocksdb_writeoptions_t* woptions) {
303   char* err = NULL;
304   db = rocksdb_open(options, dbname, &err);
305   CheckNoError(err);
306   rocksdb_put(db, woptions, "foo", 3, "foovalue", 8, &err);
307   CheckNoError(err);
308   CheckGet(db, roptions, "foo", "foovalue");
309   rocksdb_put(db, woptions, "bar", 3, "barvalue", 8, &err);
310   CheckNoError(err);
311   CheckGet(db, roptions, "bar", "barvalue");
312   rocksdb_put(db, woptions, "baz", 3, "bazvalue", 8, &err);
313   CheckNoError(err);
314   CheckGet(db, roptions, "baz", "bazvalue");
315 
316   // Force compaction
317   rocksdb_compact_range(db, NULL, 0, NULL, 0);
318   // should have filtered bar, but not foo
319   CheckGet(db, roptions, "foo", "foovalue");
320   CheckGet(db, roptions, "bar", NULL);
321   CheckGet(db, roptions, "baz", "newbazvalue");
322   return db;
323 }
324 
325 // Custom merge operator
MergeOperatorDestroy(void * arg)326 static void MergeOperatorDestroy(void* arg) { (void)arg; }
MergeOperatorName(void * arg)327 static const char* MergeOperatorName(void* arg) {
328   (void)arg;
329   return "TestMergeOperator";
330 }
MergeOperatorFullMerge(void * arg,const char * key,size_t key_length,const char * existing_value,size_t existing_value_length,const char * const * operands_list,const size_t * operands_list_length,int num_operands,unsigned char * success,size_t * new_value_length)331 static char* MergeOperatorFullMerge(
332     void* arg,
333     const char* key, size_t key_length,
334     const char* existing_value, size_t existing_value_length,
335     const char* const* operands_list, const size_t* operands_list_length,
336     int num_operands,
337     unsigned char* success, size_t* new_value_length) {
338   (void)arg;
339   (void)key;
340   (void)key_length;
341   (void)existing_value;
342   (void)existing_value_length;
343   (void)operands_list;
344   (void)operands_list_length;
345   (void)num_operands;
346   *new_value_length = 4;
347   *success = 1;
348   char* result = malloc(4);
349   memcpy(result, "fake", 4);
350   return result;
351 }
MergeOperatorPartialMerge(void * arg,const char * key,size_t key_length,const char * const * operands_list,const size_t * operands_list_length,int num_operands,unsigned char * success,size_t * new_value_length)352 static char* MergeOperatorPartialMerge(
353     void* arg,
354     const char* key, size_t key_length,
355     const char* const* operands_list, const size_t* operands_list_length,
356     int num_operands,
357     unsigned char* success, size_t* new_value_length) {
358   (void)arg;
359   (void)key;
360   (void)key_length;
361   (void)operands_list;
362   (void)operands_list_length;
363   (void)num_operands;
364   *new_value_length = 4;
365   *success = 1;
366   char* result = malloc(4);
367   memcpy(result, "fake", 4);
368   return result;
369 }
370 
CheckTxnGet(rocksdb_transaction_t * txn,const rocksdb_readoptions_t * options,const char * key,const char * expected)371 static void CheckTxnGet(
372         rocksdb_transaction_t* txn,
373         const rocksdb_readoptions_t* options,
374         const char* key,
375         const char* expected) {
376         char* err = NULL;
377         size_t val_len;
378         char* val;
379         val = rocksdb_transaction_get(txn, options, key, strlen(key), &val_len, &err);
380         CheckNoError(err);
381         CheckEqual(expected, val, val_len);
382         Free(&val);
383 }
384 
CheckTxnGetCF(rocksdb_transaction_t * txn,const rocksdb_readoptions_t * options,rocksdb_column_family_handle_t * column_family,const char * key,const char * expected)385 static void CheckTxnGetCF(rocksdb_transaction_t* txn,
386                           const rocksdb_readoptions_t* options,
387                           rocksdb_column_family_handle_t* column_family,
388                           const char* key, const char* expected) {
389   char* err = NULL;
390   size_t val_len;
391   char* val;
392   val = rocksdb_transaction_get_cf(txn, options, column_family, key,
393                                    strlen(key), &val_len, &err);
394   CheckNoError(err);
395   CheckEqual(expected, val, val_len);
396   Free(&val);
397 }
398 
CheckTxnDBGet(rocksdb_transactiondb_t * txn_db,const rocksdb_readoptions_t * options,const char * key,const char * expected)399 static void CheckTxnDBGet(
400         rocksdb_transactiondb_t* txn_db,
401         const rocksdb_readoptions_t* options,
402         const char* key,
403         const char* expected) {
404         char* err = NULL;
405         size_t val_len;
406         char* val;
407         val = rocksdb_transactiondb_get(txn_db, options, key, strlen(key), &val_len, &err);
408         CheckNoError(err);
409         CheckEqual(expected, val, val_len);
410         Free(&val);
411 }
412 
CheckTxnDBGetCF(rocksdb_transactiondb_t * txn_db,const rocksdb_readoptions_t * options,rocksdb_column_family_handle_t * column_family,const char * key,const char * expected)413 static void CheckTxnDBGetCF(rocksdb_transactiondb_t* txn_db,
414                             const rocksdb_readoptions_t* options,
415                             rocksdb_column_family_handle_t* column_family,
416                             const char* key, const char* expected) {
417   char* err = NULL;
418   size_t val_len;
419   char* val;
420   val = rocksdb_transactiondb_get_cf(txn_db, options, column_family, key,
421                                      strlen(key), &val_len, &err);
422   CheckNoError(err);
423   CheckEqual(expected, val, val_len);
424   Free(&val);
425 }
426 
main(int argc,char ** argv)427 int main(int argc, char** argv) {
428   (void)argc;
429   (void)argv;
430   rocksdb_t* db;
431   rocksdb_comparator_t* cmp;
432   rocksdb_cache_t* cache;
433   rocksdb_dbpath_t *dbpath;
434   rocksdb_env_t* env;
435   rocksdb_options_t* options;
436   rocksdb_compactoptions_t* coptions;
437   rocksdb_block_based_table_options_t* table_options;
438   rocksdb_readoptions_t* roptions;
439   rocksdb_writeoptions_t* woptions;
440   rocksdb_ratelimiter_t* rate_limiter;
441   rocksdb_transactiondb_t* txn_db;
442   rocksdb_transactiondb_options_t* txn_db_options;
443   rocksdb_transaction_t* txn;
444   rocksdb_transaction_options_t* txn_options;
445   rocksdb_optimistictransactiondb_t* otxn_db;
446   rocksdb_optimistictransaction_options_t* otxn_options;
447   char* err = NULL;
448   int run = -1;
449 
450   snprintf(dbname, sizeof(dbname),
451            "%s/rocksdb_c_test-%d",
452            GetTempDir(),
453            ((int) geteuid()));
454 
455   snprintf(dbbackupname, sizeof(dbbackupname),
456            "%s/rocksdb_c_test-%d-backup",
457            GetTempDir(),
458            ((int) geteuid()));
459 
460   snprintf(dbcheckpointname, sizeof(dbcheckpointname),
461            "%s/rocksdb_c_test-%d-checkpoint",
462            GetTempDir(),
463            ((int) geteuid()));
464 
465   snprintf(sstfilename, sizeof(sstfilename),
466            "%s/rocksdb_c_test-%d-sst",
467            GetTempDir(),
468            ((int)geteuid()));
469 
470   snprintf(dbpathname, sizeof(dbpathname),
471            "%s/rocksdb_c_test-%d-dbpath",
472            GetTempDir(),
473            ((int) geteuid()));
474 
475   StartPhase("create_objects");
476   cmp = rocksdb_comparator_create(NULL, CmpDestroy, CmpCompare, CmpName);
477   dbpath = rocksdb_dbpath_create(dbpathname, 1024 * 1024);
478   env = rocksdb_create_default_env();
479   cache = rocksdb_cache_create_lru(100000);
480 
481   options = rocksdb_options_create();
482   rocksdb_options_set_comparator(options, cmp);
483   rocksdb_options_set_error_if_exists(options, 1);
484   rocksdb_options_set_env(options, env);
485   rocksdb_options_set_info_log(options, NULL);
486   rocksdb_options_set_write_buffer_size(options, 100000);
487   rocksdb_options_set_paranoid_checks(options, 1);
488   rocksdb_options_set_max_open_files(options, 10);
489   rocksdb_options_set_base_background_compactions(options, 1);
490 
491   table_options = rocksdb_block_based_options_create();
492   rocksdb_block_based_options_set_block_cache(table_options, cache);
493   rocksdb_block_based_options_set_data_block_index_type(table_options, 1);
494   rocksdb_block_based_options_set_data_block_hash_ratio(table_options, 0.75);
495   rocksdb_options_set_block_based_table_factory(options, table_options);
496 
497   rocksdb_options_set_compression(options, rocksdb_no_compression);
498   rocksdb_options_set_compression_options(options, -14, -1, 0, 0);
499   int compression_levels[] = {rocksdb_no_compression, rocksdb_no_compression,
500                               rocksdb_no_compression, rocksdb_no_compression};
501   rocksdb_options_set_compression_per_level(options, compression_levels, 4);
502   rate_limiter = rocksdb_ratelimiter_create(1000 * 1024 * 1024, 100 * 1000, 10);
503   rocksdb_options_set_ratelimiter(options, rate_limiter);
504   rocksdb_ratelimiter_destroy(rate_limiter);
505 
506   roptions = rocksdb_readoptions_create();
507   rocksdb_readoptions_set_verify_checksums(roptions, 1);
508   rocksdb_readoptions_set_fill_cache(roptions, 1);
509 
510   woptions = rocksdb_writeoptions_create();
511   rocksdb_writeoptions_set_sync(woptions, 1);
512 
513   coptions = rocksdb_compactoptions_create();
514   rocksdb_compactoptions_set_exclusive_manual_compaction(coptions, 1);
515 
516   StartPhase("destroy");
517   rocksdb_destroy_db(options, dbname, &err);
518   Free(&err);
519 
520   StartPhase("open_error");
521   rocksdb_open(options, dbname, &err);
522   CheckCondition(err != NULL);
523   Free(&err);
524 
525   StartPhase("open");
526   rocksdb_options_set_create_if_missing(options, 1);
527   db = rocksdb_open(options, dbname, &err);
528   CheckNoError(err);
529   CheckGet(db, roptions, "foo", NULL);
530 
531   StartPhase("put");
532   rocksdb_put(db, woptions, "foo", 3, "hello", 5, &err);
533   CheckNoError(err);
534   CheckGet(db, roptions, "foo", "hello");
535 
536   StartPhase("backup_and_restore");
537   {
538     rocksdb_destroy_db(options, dbbackupname, &err);
539     CheckNoError(err);
540 
541     rocksdb_backup_engine_t *be = rocksdb_backup_engine_open(options, dbbackupname, &err);
542     CheckNoError(err);
543 
544     rocksdb_backup_engine_create_new_backup(be, db, &err);
545     CheckNoError(err);
546 
547     // need a change to trigger a new backup
548     rocksdb_delete(db, woptions, "does-not-exist", 14, &err);
549     CheckNoError(err);
550 
551     rocksdb_backup_engine_create_new_backup(be, db, &err);
552     CheckNoError(err);
553 
554     const rocksdb_backup_engine_info_t* bei = rocksdb_backup_engine_get_backup_info(be);
555     CheckCondition(rocksdb_backup_engine_info_count(bei) > 1);
556     rocksdb_backup_engine_info_destroy(bei);
557 
558     rocksdb_backup_engine_purge_old_backups(be, 1, &err);
559     CheckNoError(err);
560 
561     bei = rocksdb_backup_engine_get_backup_info(be);
562     CheckCondition(rocksdb_backup_engine_info_count(bei) == 1);
563     rocksdb_backup_engine_info_destroy(bei);
564 
565     rocksdb_delete(db, woptions, "foo", 3, &err);
566     CheckNoError(err);
567 
568     rocksdb_close(db);
569 
570     rocksdb_destroy_db(options, dbname, &err);
571     CheckNoError(err);
572 
573     rocksdb_restore_options_t *restore_options = rocksdb_restore_options_create();
574     rocksdb_restore_options_set_keep_log_files(restore_options, 0);
575     rocksdb_backup_engine_restore_db_from_latest_backup(be, dbname, dbname, restore_options, &err);
576     CheckNoError(err);
577     rocksdb_restore_options_destroy(restore_options);
578 
579     rocksdb_options_set_error_if_exists(options, 0);
580     db = rocksdb_open(options, dbname, &err);
581     CheckNoError(err);
582     rocksdb_options_set_error_if_exists(options, 1);
583 
584     CheckGet(db, roptions, "foo", "hello");
585 
586     rocksdb_backup_engine_close(be);
587   }
588 
589   StartPhase("checkpoint");
590   {
591     rocksdb_destroy_db(options, dbcheckpointname, &err);
592     CheckNoError(err);
593 
594     rocksdb_checkpoint_t* checkpoint = rocksdb_checkpoint_object_create(db, &err);
595     CheckNoError(err);
596 
597     rocksdb_checkpoint_create(checkpoint, dbcheckpointname, 0, &err);
598     CheckNoError(err);
599 
600     // start a new database from the checkpoint
601     rocksdb_close(db);
602     rocksdb_options_set_error_if_exists(options, 0);
603     db = rocksdb_open(options, dbcheckpointname, &err);
604     CheckNoError(err);
605 
606     CheckGet(db, roptions, "foo", "hello");
607 
608     rocksdb_checkpoint_object_destroy(checkpoint);
609 
610     rocksdb_close(db);
611     rocksdb_destroy_db(options, dbcheckpointname, &err);
612     CheckNoError(err);
613 
614     db = rocksdb_open(options, dbname, &err);
615     CheckNoError(err);
616     rocksdb_options_set_error_if_exists(options, 1);
617   }
618 
619   StartPhase("compactall");
620   rocksdb_compact_range(db, NULL, 0, NULL, 0);
621   CheckGet(db, roptions, "foo", "hello");
622 
623   StartPhase("compactrange");
624   rocksdb_compact_range(db, "a", 1, "z", 1);
625   CheckGet(db, roptions, "foo", "hello");
626 
627   StartPhase("compactallopt");
628   rocksdb_compact_range_opt(db, coptions, NULL, 0, NULL, 0);
629   CheckGet(db, roptions, "foo", "hello");
630 
631   StartPhase("compactrangeopt");
632   rocksdb_compact_range_opt(db, coptions, "a", 1, "z", 1);
633   CheckGet(db, roptions, "foo", "hello");
634 
635   // Simple check cache usage
636   StartPhase("cache_usage");
637   {
638     rocksdb_readoptions_set_pin_data(roptions, 1);
639     rocksdb_iterator_t* iter = rocksdb_create_iterator(db, roptions);
640     rocksdb_iter_seek(iter, "foo", 3);
641 
642     size_t usage = rocksdb_cache_get_usage(cache);
643     CheckCondition(usage > 0);
644 
645     size_t pin_usage = rocksdb_cache_get_pinned_usage(cache);
646     CheckCondition(pin_usage > 0);
647 
648     rocksdb_iter_next(iter);
649     rocksdb_iter_destroy(iter);
650     rocksdb_readoptions_set_pin_data(roptions, 0);
651   }
652 
653   StartPhase("addfile");
654   {
655     rocksdb_envoptions_t* env_opt = rocksdb_envoptions_create();
656     rocksdb_options_t* io_options = rocksdb_options_create();
657     rocksdb_sstfilewriter_t* writer =
658         rocksdb_sstfilewriter_create(env_opt, io_options);
659 
660     remove(sstfilename);
661     rocksdb_sstfilewriter_open(writer, sstfilename, &err);
662     CheckNoError(err);
663     rocksdb_sstfilewriter_put(writer, "sstk1", 5, "v1", 2, &err);
664     CheckNoError(err);
665     rocksdb_sstfilewriter_put(writer, "sstk2", 5, "v2", 2, &err);
666     CheckNoError(err);
667     rocksdb_sstfilewriter_put(writer, "sstk3", 5, "v3", 2, &err);
668     CheckNoError(err);
669     rocksdb_sstfilewriter_finish(writer, &err);
670     CheckNoError(err);
671 
672     rocksdb_ingestexternalfileoptions_t* ing_opt =
673         rocksdb_ingestexternalfileoptions_create();
674     const char* file_list[1] = {sstfilename};
675     rocksdb_ingest_external_file(db, file_list, 1, ing_opt, &err);
676     CheckNoError(err);
677     CheckGet(db, roptions, "sstk1", "v1");
678     CheckGet(db, roptions, "sstk2", "v2");
679     CheckGet(db, roptions, "sstk3", "v3");
680 
681     remove(sstfilename);
682     rocksdb_sstfilewriter_open(writer, sstfilename, &err);
683     CheckNoError(err);
684     rocksdb_sstfilewriter_put(writer, "sstk2", 5, "v4", 2, &err);
685     CheckNoError(err);
686     rocksdb_sstfilewriter_put(writer, "sstk22", 6, "v5", 2, &err);
687     CheckNoError(err);
688     rocksdb_sstfilewriter_put(writer, "sstk3", 5, "v6", 2, &err);
689     CheckNoError(err);
690     rocksdb_sstfilewriter_finish(writer, &err);
691     CheckNoError(err);
692 
693     rocksdb_ingest_external_file(db, file_list, 1, ing_opt, &err);
694     CheckNoError(err);
695     CheckGet(db, roptions, "sstk1", "v1");
696     CheckGet(db, roptions, "sstk2", "v4");
697     CheckGet(db, roptions, "sstk22", "v5");
698     CheckGet(db, roptions, "sstk3", "v6");
699 
700     rocksdb_ingestexternalfileoptions_destroy(ing_opt);
701     rocksdb_sstfilewriter_destroy(writer);
702     rocksdb_options_destroy(io_options);
703     rocksdb_envoptions_destroy(env_opt);
704 
705     // Delete all keys we just ingested
706     rocksdb_delete(db, woptions, "sstk1", 5, &err);
707     CheckNoError(err);
708     rocksdb_delete(db, woptions, "sstk2", 5, &err);
709     CheckNoError(err);
710     rocksdb_delete(db, woptions, "sstk22", 6, &err);
711     CheckNoError(err);
712     rocksdb_delete(db, woptions, "sstk3", 5, &err);
713     CheckNoError(err);
714   }
715 
716   StartPhase("writebatch");
717   {
718     rocksdb_writebatch_t* wb = rocksdb_writebatch_create();
719     rocksdb_writebatch_put(wb, "foo", 3, "a", 1);
720     rocksdb_writebatch_clear(wb);
721     rocksdb_writebatch_put(wb, "bar", 3, "b", 1);
722     rocksdb_writebatch_put(wb, "box", 3, "c", 1);
723     rocksdb_writebatch_delete(wb, "bar", 3);
724     rocksdb_write(db, woptions, wb, &err);
725     CheckNoError(err);
726     CheckGet(db, roptions, "foo", "hello");
727     CheckGet(db, roptions, "bar", NULL);
728     CheckGet(db, roptions, "box", "c");
729     int pos = 0;
730     rocksdb_writebatch_iterate(wb, &pos, CheckPut, CheckDel);
731     CheckCondition(pos == 3);
732     rocksdb_writebatch_clear(wb);
733     rocksdb_writebatch_put(wb, "bar", 3, "b", 1);
734     rocksdb_writebatch_put(wb, "bay", 3, "d", 1);
735     rocksdb_writebatch_delete_range(wb, "bar", 3, "bay", 3);
736     rocksdb_write(db, woptions, wb, &err);
737     CheckNoError(err);
738     CheckGet(db, roptions, "bar", NULL);
739     CheckGet(db, roptions, "bay", "d");
740     rocksdb_writebatch_clear(wb);
741     const char* start_list[1] = {"bay"};
742     const size_t start_sizes[1] = {3};
743     const char* end_list[1] = {"baz"};
744     const size_t end_sizes[1] = {3};
745     rocksdb_writebatch_delete_rangev(wb, 1, start_list, start_sizes, end_list,
746                                      end_sizes);
747     rocksdb_write(db, woptions, wb, &err);
748     CheckNoError(err);
749     CheckGet(db, roptions, "bay", NULL);
750     rocksdb_writebatch_destroy(wb);
751   }
752 
753   StartPhase("writebatch_vectors");
754   {
755     rocksdb_writebatch_t* wb = rocksdb_writebatch_create();
756     const char* k_list[2] = { "z", "ap" };
757     const size_t k_sizes[2] = { 1, 2 };
758     const char* v_list[3] = { "x", "y", "z" };
759     const size_t v_sizes[3] = { 1, 1, 1 };
760     rocksdb_writebatch_putv(wb, 2, k_list, k_sizes, 3, v_list, v_sizes);
761     rocksdb_write(db, woptions, wb, &err);
762     CheckNoError(err);
763     CheckGet(db, roptions, "zap", "xyz");
764     rocksdb_writebatch_delete(wb, "zap", 3);
765     rocksdb_write(db, woptions, wb, &err);
766     CheckNoError(err);
767     CheckGet(db, roptions, "zap", NULL);
768     rocksdb_writebatch_destroy(wb);
769   }
770 
771   StartPhase("writebatch_savepoint");
772   {
773     rocksdb_writebatch_t* wb = rocksdb_writebatch_create();
774     rocksdb_writebatch_set_save_point(wb);
775     rocksdb_writebatch_set_save_point(wb);
776     const char* k_list[2] = {"z", "ap"};
777     const size_t k_sizes[2] = {1, 2};
778     const char* v_list[3] = {"x", "y", "z"};
779     const size_t v_sizes[3] = {1, 1, 1};
780     rocksdb_writebatch_pop_save_point(wb, &err);
781     CheckNoError(err);
782     rocksdb_writebatch_putv(wb, 2, k_list, k_sizes, 3, v_list, v_sizes);
783     rocksdb_writebatch_rollback_to_save_point(wb, &err);
784     CheckNoError(err);
785     rocksdb_write(db, woptions, wb, &err);
786     CheckNoError(err);
787     CheckGet(db, roptions, "zap", NULL);
788     rocksdb_writebatch_destroy(wb);
789   }
790 
791   StartPhase("writebatch_rep");
792   {
793     rocksdb_writebatch_t* wb1 = rocksdb_writebatch_create();
794     rocksdb_writebatch_put(wb1, "baz", 3, "d", 1);
795     rocksdb_writebatch_put(wb1, "quux", 4, "e", 1);
796     rocksdb_writebatch_delete(wb1, "quux", 4);
797     size_t repsize1 = 0;
798     const char* rep = rocksdb_writebatch_data(wb1, &repsize1);
799     rocksdb_writebatch_t* wb2 = rocksdb_writebatch_create_from(rep, repsize1);
800     CheckCondition(rocksdb_writebatch_count(wb1) ==
801                    rocksdb_writebatch_count(wb2));
802     size_t repsize2 = 0;
803     CheckCondition(
804         memcmp(rep, rocksdb_writebatch_data(wb2, &repsize2), repsize1) == 0);
805     rocksdb_writebatch_destroy(wb1);
806     rocksdb_writebatch_destroy(wb2);
807   }
808 
809   StartPhase("writebatch_wi");
810   {
811     rocksdb_writebatch_wi_t* wbi = rocksdb_writebatch_wi_create(0, 1);
812     rocksdb_writebatch_wi_put(wbi, "foo", 3, "a", 1);
813     rocksdb_writebatch_wi_clear(wbi);
814     rocksdb_writebatch_wi_put(wbi, "bar", 3, "b", 1);
815     rocksdb_writebatch_wi_put(wbi, "box", 3, "c", 1);
816     rocksdb_writebatch_wi_delete(wbi, "bar", 3);
817     int count = rocksdb_writebatch_wi_count(wbi);
818     CheckCondition(count == 3);
819     size_t size;
820     char* value;
821     value = rocksdb_writebatch_wi_get_from_batch(wbi, options, "box", 3, &size, &err);
822     CheckValue(err, "c", &value, size);
823     value = rocksdb_writebatch_wi_get_from_batch(wbi, options, "bar", 3, &size, &err);
824     CheckValue(err, NULL, &value, size);
825     value = rocksdb_writebatch_wi_get_from_batch_and_db(wbi, db, roptions, "foo", 3, &size, &err);
826     CheckValue(err, "hello", &value, size);
827     value = rocksdb_writebatch_wi_get_from_batch_and_db(wbi, db, roptions, "box", 3, &size, &err);
828     CheckValue(err, "c", &value, size);
829     rocksdb_write_writebatch_wi(db, woptions, wbi, &err);
830     CheckNoError(err);
831     CheckGet(db, roptions, "foo", "hello");
832     CheckGet(db, roptions, "bar", NULL);
833     CheckGet(db, roptions, "box", "c");
834     int pos = 0;
835     rocksdb_writebatch_wi_iterate(wbi, &pos, CheckPut, CheckDel);
836     CheckCondition(pos == 3);
837     rocksdb_writebatch_wi_clear(wbi);
838     rocksdb_writebatch_wi_destroy(wbi);
839   }
840 
841   StartPhase("writebatch_wi_vectors");
842   {
843     rocksdb_writebatch_wi_t* wb = rocksdb_writebatch_wi_create(0, 1);
844     const char* k_list[2] = { "z", "ap" };
845     const size_t k_sizes[2] = { 1, 2 };
846     const char* v_list[3] = { "x", "y", "z" };
847     const size_t v_sizes[3] = { 1, 1, 1 };
848     rocksdb_writebatch_wi_putv(wb, 2, k_list, k_sizes, 3, v_list, v_sizes);
849     rocksdb_write_writebatch_wi(db, woptions, wb, &err);
850     CheckNoError(err);
851     CheckGet(db, roptions, "zap", "xyz");
852     rocksdb_writebatch_wi_delete(wb, "zap", 3);
853     rocksdb_write_writebatch_wi(db, woptions, wb, &err);
854     CheckNoError(err);
855     CheckGet(db, roptions, "zap", NULL);
856     rocksdb_writebatch_wi_destroy(wb);
857   }
858 
859   StartPhase("writebatch_wi_savepoint");
860   {
861     rocksdb_writebatch_wi_t* wb = rocksdb_writebatch_wi_create(0, 1);
862     rocksdb_writebatch_wi_set_save_point(wb);
863     const char* k_list[2] = {"z", "ap"};
864     const size_t k_sizes[2] = {1, 2};
865     const char* v_list[3] = {"x", "y", "z"};
866     const size_t v_sizes[3] = {1, 1, 1};
867     rocksdb_writebatch_wi_putv(wb, 2, k_list, k_sizes, 3, v_list, v_sizes);
868     rocksdb_writebatch_wi_rollback_to_save_point(wb, &err);
869     CheckNoError(err);
870     rocksdb_write_writebatch_wi(db, woptions, wb, &err);
871     CheckNoError(err);
872     CheckGet(db, roptions, "zap", NULL);
873     rocksdb_writebatch_wi_destroy(wb);
874   }
875 
876   StartPhase("iter");
877   {
878     rocksdb_iterator_t* iter = rocksdb_create_iterator(db, roptions);
879     CheckCondition(!rocksdb_iter_valid(iter));
880     rocksdb_iter_seek_to_first(iter);
881     CheckCondition(rocksdb_iter_valid(iter));
882     CheckIter(iter, "box", "c");
883     rocksdb_iter_next(iter);
884     CheckIter(iter, "foo", "hello");
885     rocksdb_iter_prev(iter);
886     CheckIter(iter, "box", "c");
887     rocksdb_iter_prev(iter);
888     CheckCondition(!rocksdb_iter_valid(iter));
889     rocksdb_iter_seek_to_last(iter);
890     CheckIter(iter, "foo", "hello");
891     rocksdb_iter_seek(iter, "b", 1);
892     CheckIter(iter, "box", "c");
893     rocksdb_iter_seek_for_prev(iter, "g", 1);
894     CheckIter(iter, "foo", "hello");
895     rocksdb_iter_seek_for_prev(iter, "box", 3);
896     CheckIter(iter, "box", "c");
897     rocksdb_iter_get_error(iter, &err);
898     CheckNoError(err);
899     rocksdb_iter_destroy(iter);
900   }
901 
902   StartPhase("wbwi_iter");
903   {
904     rocksdb_iterator_t* base_iter = rocksdb_create_iterator(db, roptions);
905     rocksdb_writebatch_wi_t* wbi = rocksdb_writebatch_wi_create(0, 1);
906     rocksdb_writebatch_wi_put(wbi, "bar", 3, "b", 1);
907     rocksdb_writebatch_wi_delete(wbi, "foo", 3);
908     rocksdb_iterator_t* iter =
909         rocksdb_writebatch_wi_create_iterator_with_base(wbi, base_iter);
910     CheckCondition(!rocksdb_iter_valid(iter));
911     rocksdb_iter_seek_to_first(iter);
912     CheckCondition(rocksdb_iter_valid(iter));
913     CheckIter(iter, "bar", "b");
914     rocksdb_iter_next(iter);
915     CheckIter(iter, "box", "c");
916     rocksdb_iter_prev(iter);
917     CheckIter(iter, "bar", "b");
918     rocksdb_iter_prev(iter);
919     CheckCondition(!rocksdb_iter_valid(iter));
920     rocksdb_iter_seek_to_last(iter);
921     CheckIter(iter, "box", "c");
922     rocksdb_iter_seek(iter, "b", 1);
923     CheckIter(iter, "bar", "b");
924     rocksdb_iter_seek_for_prev(iter, "c", 1);
925     CheckIter(iter, "box", "c");
926     rocksdb_iter_seek_for_prev(iter, "box", 3);
927     CheckIter(iter, "box", "c");
928     rocksdb_iter_get_error(iter, &err);
929     CheckNoError(err);
930     rocksdb_iter_destroy(iter);
931     rocksdb_writebatch_wi_destroy(wbi);
932   }
933 
934   StartPhase("multiget");
935   {
936     const char* keys[3] = { "box", "foo", "notfound" };
937     const size_t keys_sizes[3] = { 3, 3, 8 };
938     char* vals[3];
939     size_t vals_sizes[3];
940     char* errs[3];
941     rocksdb_multi_get(db, roptions, 3, keys, keys_sizes, vals, vals_sizes, errs);
942 
943     int i;
944     for (i = 0; i < 3; i++) {
945       CheckEqual(NULL, errs[i], 0);
946       switch (i) {
947       case 0:
948         CheckEqual("c", vals[i], vals_sizes[i]);
949         break;
950       case 1:
951         CheckEqual("hello", vals[i], vals_sizes[i]);
952         break;
953       case 2:
954         CheckEqual(NULL, vals[i], vals_sizes[i]);
955         break;
956       }
957       Free(&vals[i]);
958     }
959   }
960 
961   StartPhase("pin_get");
962   {
963     CheckPinGet(db, roptions, "box", "c");
964     CheckPinGet(db, roptions, "foo", "hello");
965     CheckPinGet(db, roptions, "notfound", NULL);
966   }
967 
968   StartPhase("approximate_sizes");
969   {
970     int i;
971     int n = 20000;
972     char keybuf[100];
973     char valbuf[100];
974     uint64_t sizes[2];
975     const char* start[2] = { "a", "k00000000000000010000" };
976     size_t start_len[2] = { 1, 21 };
977     const char* limit[2] = { "k00000000000000010000", "z" };
978     size_t limit_len[2] = { 21, 1 };
979     rocksdb_writeoptions_set_sync(woptions, 0);
980     for (i = 0; i < n; i++) {
981       snprintf(keybuf, sizeof(keybuf), "k%020d", i);
982       snprintf(valbuf, sizeof(valbuf), "v%020d", i);
983       rocksdb_put(db, woptions, keybuf, strlen(keybuf), valbuf, strlen(valbuf),
984                   &err);
985       CheckNoError(err);
986     }
987     rocksdb_approximate_sizes(db, 2, start, start_len, limit, limit_len, sizes);
988     CheckCondition(sizes[0] > 0);
989     CheckCondition(sizes[1] > 0);
990   }
991 
992   StartPhase("property");
993   {
994     char* prop = rocksdb_property_value(db, "nosuchprop");
995     CheckCondition(prop == NULL);
996     prop = rocksdb_property_value(db, "rocksdb.stats");
997     CheckCondition(prop != NULL);
998     Free(&prop);
999   }
1000 
1001   StartPhase("snapshot");
1002   {
1003     const rocksdb_snapshot_t* snap;
1004     snap = rocksdb_create_snapshot(db);
1005     rocksdb_delete(db, woptions, "foo", 3, &err);
1006     CheckNoError(err);
1007     rocksdb_readoptions_set_snapshot(roptions, snap);
1008     CheckGet(db, roptions, "foo", "hello");
1009     rocksdb_readoptions_set_snapshot(roptions, NULL);
1010     CheckGet(db, roptions, "foo", NULL);
1011     rocksdb_release_snapshot(db, snap);
1012   }
1013 
1014   StartPhase("repair");
1015   {
1016     // If we do not compact here, then the lazy deletion of
1017     // files (https://reviews.facebook.net/D6123) would leave
1018     // around deleted files and the repair process will find
1019     // those files and put them back into the database.
1020     rocksdb_compact_range(db, NULL, 0, NULL, 0);
1021     rocksdb_close(db);
1022     rocksdb_options_set_create_if_missing(options, 0);
1023     rocksdb_options_set_error_if_exists(options, 0);
1024     rocksdb_options_set_wal_recovery_mode(options, 2);
1025     rocksdb_repair_db(options, dbname, &err);
1026     CheckNoError(err);
1027     db = rocksdb_open(options, dbname, &err);
1028     CheckNoError(err);
1029     CheckGet(db, roptions, "foo", NULL);
1030     CheckGet(db, roptions, "bar", NULL);
1031     CheckGet(db, roptions, "box", "c");
1032     rocksdb_options_set_create_if_missing(options, 1);
1033     rocksdb_options_set_error_if_exists(options, 1);
1034   }
1035 
1036   StartPhase("filter");
1037   for (run = 0; run <= 2; run++) {
1038     // First run uses custom filter
1039     // Second run uses old block-based bloom filter
1040     // Third run uses full bloom filter
1041     CheckNoError(err);
1042     rocksdb_filterpolicy_t* policy;
1043     if (run == 0) {
1044       policy = rocksdb_filterpolicy_create(NULL, FilterDestroy, FilterCreate,
1045                                            FilterKeyMatch, NULL, FilterName);
1046     } else if (run == 1) {
1047       policy = rocksdb_filterpolicy_create_bloom(8);
1048     } else {
1049       policy = rocksdb_filterpolicy_create_bloom_full(8);
1050     }
1051     rocksdb_block_based_options_set_filter_policy(table_options, policy);
1052 
1053     // Create new database
1054     rocksdb_close(db);
1055     rocksdb_destroy_db(options, dbname, &err);
1056     rocksdb_options_set_block_based_table_factory(options, table_options);
1057     db = rocksdb_open(options, dbname, &err);
1058     CheckNoError(err);
1059     rocksdb_put(db, woptions, "foo", 3, "foovalue", 8, &err);
1060     CheckNoError(err);
1061     rocksdb_put(db, woptions, "bar", 3, "barvalue", 8, &err);
1062     CheckNoError(err);
1063 
1064     {
1065       // Add enough keys to get just one reasonably populated Bloom filter
1066       const int keys_to_add = 1500;
1067       int i;
1068       char keybuf[100];
1069       for (i = 0; i < keys_to_add; i++) {
1070         snprintf(keybuf, sizeof(keybuf), "yes%020d", i);
1071         rocksdb_put(db, woptions, keybuf, strlen(keybuf), "val", 3, &err);
1072         CheckNoError(err);
1073       }
1074     }
1075     rocksdb_compact_range(db, NULL, 0, NULL, 0);
1076 
1077     fake_filter_result = 1;
1078     CheckGet(db, roptions, "foo", "foovalue");
1079     CheckGet(db, roptions, "bar", "barvalue");
1080     if (run == 0) {
1081       // Must not find value when custom filter returns false
1082       fake_filter_result = 0;
1083       CheckGet(db, roptions, "foo", NULL);
1084       CheckGet(db, roptions, "bar", NULL);
1085       fake_filter_result = 1;
1086 
1087       CheckGet(db, roptions, "foo", "foovalue");
1088       CheckGet(db, roptions, "bar", "barvalue");
1089     }
1090 
1091     {
1092       // Query some keys not added to identify Bloom filter implementation
1093       // from false positive queries, using perfcontext to detect Bloom
1094       // filter behavior
1095       rocksdb_perfcontext_t* perf = rocksdb_perfcontext_create();
1096       rocksdb_perfcontext_reset(perf);
1097 
1098       const int keys_to_query = 10000;
1099       int i;
1100       char keybuf[100];
1101       for (i = 0; i < keys_to_query; i++) {
1102         fake_filter_result = i % 2;
1103         snprintf(keybuf, sizeof(keybuf), "no%020d", i);
1104         CheckGet(db, roptions, keybuf, NULL);
1105       }
1106 
1107       const int hits =
1108           (int)rocksdb_perfcontext_metric(perf, rocksdb_bloom_sst_hit_count);
1109       if (run == 0) {
1110         // Due to half true, half false with fake filter result
1111         CheckCondition(hits == keys_to_query / 2);
1112       } else if (run == 1) {
1113         // Essentially a fingerprint of the block-based Bloom schema
1114         CheckCondition(hits == 241);
1115       } else {
1116         // Essentially a fingerprint of the full Bloom schema(s),
1117         // format_version < 5, which vary for three different CACHE_LINE_SIZEs
1118         CheckCondition(hits == 224 || hits == 180 || hits == 125);
1119       }
1120       CheckCondition(
1121           (keys_to_query - hits) ==
1122           (int)rocksdb_perfcontext_metric(perf, rocksdb_bloom_sst_miss_count));
1123 
1124       rocksdb_perfcontext_destroy(perf);
1125     }
1126 
1127     // Reset the policy
1128     rocksdb_block_based_options_set_filter_policy(table_options, NULL);
1129     rocksdb_options_set_block_based_table_factory(options, table_options);
1130   }
1131 
1132   StartPhase("compaction_filter");
1133   {
1134     rocksdb_options_t* options_with_filter = rocksdb_options_create();
1135     rocksdb_options_set_create_if_missing(options_with_filter, 1);
1136     rocksdb_compactionfilter_t* cfilter;
1137     cfilter = rocksdb_compactionfilter_create(NULL, CFilterDestroy,
1138                                               CFilterFilter, CFilterName);
1139     // Create new database
1140     rocksdb_close(db);
1141     rocksdb_destroy_db(options_with_filter, dbname, &err);
1142     rocksdb_options_set_compaction_filter(options_with_filter, cfilter);
1143     db = CheckCompaction(db, options_with_filter, roptions, woptions);
1144 
1145     rocksdb_options_set_compaction_filter(options_with_filter, NULL);
1146     rocksdb_compactionfilter_destroy(cfilter);
1147     rocksdb_options_destroy(options_with_filter);
1148   }
1149 
1150   StartPhase("compaction_filter_factory");
1151   {
1152     rocksdb_options_t* options_with_filter_factory = rocksdb_options_create();
1153     rocksdb_options_set_create_if_missing(options_with_filter_factory, 1);
1154     rocksdb_compactionfilterfactory_t* factory;
1155     factory = rocksdb_compactionfilterfactory_create(
1156         NULL, CFilterFactoryDestroy, CFilterCreate, CFilterFactoryName);
1157     // Create new database
1158     rocksdb_close(db);
1159     rocksdb_destroy_db(options_with_filter_factory, dbname, &err);
1160     rocksdb_options_set_compaction_filter_factory(options_with_filter_factory,
1161                                                   factory);
1162     db = CheckCompaction(db, options_with_filter_factory, roptions, woptions);
1163 
1164     rocksdb_options_set_compaction_filter_factory(
1165         options_with_filter_factory, NULL);
1166     rocksdb_options_destroy(options_with_filter_factory);
1167   }
1168 
1169   StartPhase("merge_operator");
1170   {
1171     rocksdb_mergeoperator_t* merge_operator;
1172     merge_operator = rocksdb_mergeoperator_create(
1173         NULL, MergeOperatorDestroy, MergeOperatorFullMerge,
1174         MergeOperatorPartialMerge, NULL, MergeOperatorName);
1175     // Create new database
1176     rocksdb_close(db);
1177     rocksdb_destroy_db(options, dbname, &err);
1178     rocksdb_options_set_merge_operator(options, merge_operator);
1179     db = rocksdb_open(options, dbname, &err);
1180     CheckNoError(err);
1181     rocksdb_put(db, woptions, "foo", 3, "foovalue", 8, &err);
1182     CheckNoError(err);
1183     CheckGet(db, roptions, "foo", "foovalue");
1184     rocksdb_merge(db, woptions, "foo", 3, "barvalue", 8, &err);
1185     CheckNoError(err);
1186     CheckGet(db, roptions, "foo", "fake");
1187 
1188     // Merge of a non-existing value
1189     rocksdb_merge(db, woptions, "bar", 3, "barvalue", 8, &err);
1190     CheckNoError(err);
1191     CheckGet(db, roptions, "bar", "fake");
1192 
1193   }
1194 
1195   StartPhase("columnfamilies");
1196   {
1197     rocksdb_close(db);
1198     rocksdb_destroy_db(options, dbname, &err);
1199     CheckNoError(err);
1200 
1201     rocksdb_options_t* db_options = rocksdb_options_create();
1202     rocksdb_options_set_create_if_missing(db_options, 1);
1203     db = rocksdb_open(db_options, dbname, &err);
1204     CheckNoError(err)
1205     rocksdb_column_family_handle_t* cfh;
1206     cfh = rocksdb_create_column_family(db, db_options, "cf1", &err);
1207     rocksdb_column_family_handle_destroy(cfh);
1208     CheckNoError(err);
1209     rocksdb_close(db);
1210 
1211     size_t cflen;
1212     char** column_fams = rocksdb_list_column_families(db_options, dbname, &cflen, &err);
1213     CheckNoError(err);
1214     CheckEqual("default", column_fams[0], 7);
1215     CheckEqual("cf1", column_fams[1], 3);
1216     CheckCondition(cflen == 2);
1217     rocksdb_list_column_families_destroy(column_fams, cflen);
1218 
1219     rocksdb_options_t* cf_options = rocksdb_options_create();
1220 
1221     const char* cf_names[2] = {"default", "cf1"};
1222     const rocksdb_options_t* cf_opts[2] = {cf_options, cf_options};
1223     rocksdb_column_family_handle_t* handles[2];
1224     db = rocksdb_open_column_families(db_options, dbname, 2, cf_names, cf_opts, handles, &err);
1225     CheckNoError(err);
1226 
1227     rocksdb_put_cf(db, woptions, handles[1], "foo", 3, "hello", 5, &err);
1228     CheckNoError(err);
1229 
1230     rocksdb_put_cf(db, woptions, handles[1], "foobar1", 7, "hello1", 6, &err);
1231     CheckNoError(err);
1232     rocksdb_put_cf(db, woptions, handles[1], "foobar2", 7, "hello2", 6, &err);
1233     CheckNoError(err);
1234     rocksdb_put_cf(db, woptions, handles[1], "foobar3", 7, "hello3", 6, &err);
1235     CheckNoError(err);
1236     rocksdb_put_cf(db, woptions, handles[1], "foobar4", 7, "hello4", 6, &err);
1237     CheckNoError(err);
1238 
1239     rocksdb_flushoptions_t *flush_options = rocksdb_flushoptions_create();
1240     rocksdb_flushoptions_set_wait(flush_options, 1);
1241     rocksdb_flush_cf(db, flush_options, handles[1], &err);
1242     CheckNoError(err)
1243     rocksdb_flushoptions_destroy(flush_options);
1244 
1245     CheckGetCF(db, roptions, handles[1], "foo", "hello");
1246     CheckPinGetCF(db, roptions, handles[1], "foo", "hello");
1247 
1248     rocksdb_delete_cf(db, woptions, handles[1], "foo", 3, &err);
1249     CheckNoError(err);
1250 
1251     rocksdb_delete_range_cf(db, woptions, handles[1], "foobar2", 7, "foobar4",
1252                             7, &err);
1253     CheckNoError(err);
1254 
1255     CheckGetCF(db, roptions, handles[1], "foo", NULL);
1256     CheckPinGetCF(db, roptions, handles[1], "foo", NULL);
1257 
1258     rocksdb_writebatch_t* wb = rocksdb_writebatch_create();
1259     rocksdb_writebatch_put_cf(wb, handles[1], "baz", 3, "a", 1);
1260     rocksdb_writebatch_clear(wb);
1261     rocksdb_writebatch_put_cf(wb, handles[1], "bar", 3, "b", 1);
1262     rocksdb_writebatch_put_cf(wb, handles[1], "box", 3, "c", 1);
1263     rocksdb_writebatch_delete_cf(wb, handles[1], "bar", 3);
1264     rocksdb_write(db, woptions, wb, &err);
1265     CheckNoError(err);
1266     CheckGetCF(db, roptions, handles[1], "baz", NULL);
1267     CheckGetCF(db, roptions, handles[1], "bar", NULL);
1268     CheckGetCF(db, roptions, handles[1], "box", "c");
1269     CheckPinGetCF(db, roptions, handles[1], "baz", NULL);
1270     CheckPinGetCF(db, roptions, handles[1], "bar", NULL);
1271     CheckPinGetCF(db, roptions, handles[1], "box", "c");
1272     rocksdb_writebatch_destroy(wb);
1273 
1274     const char* keys[3] = { "box", "box", "barfooxx" };
1275     const rocksdb_column_family_handle_t* get_handles[3] = { handles[0], handles[1], handles[1] };
1276     const size_t keys_sizes[3] = { 3, 3, 8 };
1277     char* vals[3];
1278     size_t vals_sizes[3];
1279     char* errs[3];
1280     rocksdb_multi_get_cf(db, roptions, get_handles, 3, keys, keys_sizes, vals, vals_sizes, errs);
1281 
1282     int i;
1283     for (i = 0; i < 3; i++) {
1284       CheckEqual(NULL, errs[i], 0);
1285       switch (i) {
1286       case 0:
1287         CheckEqual(NULL, vals[i], vals_sizes[i]); // wrong cf
1288         break;
1289       case 1:
1290         CheckEqual("c", vals[i], vals_sizes[i]); // bingo
1291         break;
1292       case 2:
1293         CheckEqual(NULL, vals[i], vals_sizes[i]); // normal not found
1294         break;
1295       }
1296       Free(&vals[i]);
1297     }
1298 
1299     rocksdb_iterator_t* iter = rocksdb_create_iterator_cf(db, roptions, handles[1]);
1300     CheckCondition(!rocksdb_iter_valid(iter));
1301     rocksdb_iter_seek_to_first(iter);
1302     CheckCondition(rocksdb_iter_valid(iter));
1303 
1304     for (i = 0; rocksdb_iter_valid(iter) != 0; rocksdb_iter_next(iter)) {
1305       i++;
1306     }
1307     CheckCondition(i == 3);
1308     rocksdb_iter_get_error(iter, &err);
1309     CheckNoError(err);
1310     rocksdb_iter_destroy(iter);
1311 
1312     rocksdb_column_family_handle_t* iters_cf_handles[2] = { handles[0], handles[1] };
1313     rocksdb_iterator_t* iters_handles[2];
1314     rocksdb_create_iterators(db, roptions, iters_cf_handles, iters_handles, 2, &err);
1315     CheckNoError(err);
1316 
1317     iter = iters_handles[0];
1318     CheckCondition(!rocksdb_iter_valid(iter));
1319     rocksdb_iter_seek_to_first(iter);
1320     CheckCondition(!rocksdb_iter_valid(iter));
1321     rocksdb_iter_destroy(iter);
1322 
1323     iter = iters_handles[1];
1324     CheckCondition(!rocksdb_iter_valid(iter));
1325     rocksdb_iter_seek_to_first(iter);
1326     CheckCondition(rocksdb_iter_valid(iter));
1327 
1328     for (i = 0; rocksdb_iter_valid(iter) != 0; rocksdb_iter_next(iter)) {
1329       i++;
1330     }
1331     CheckCondition(i == 3);
1332     rocksdb_iter_get_error(iter, &err);
1333     CheckNoError(err);
1334     rocksdb_iter_destroy(iter);
1335 
1336     rocksdb_drop_column_family(db, handles[1], &err);
1337     CheckNoError(err);
1338     for (i = 0; i < 2; i++) {
1339       rocksdb_column_family_handle_destroy(handles[i]);
1340     }
1341     rocksdb_close(db);
1342     rocksdb_destroy_db(options, dbname, &err);
1343     rocksdb_options_destroy(db_options);
1344     rocksdb_options_destroy(cf_options);
1345   }
1346 
1347   StartPhase("prefix");
1348   {
1349     // Create new database
1350     rocksdb_options_set_allow_mmap_reads(options, 1);
1351     rocksdb_options_set_prefix_extractor(options, rocksdb_slicetransform_create_fixed_prefix(3));
1352     rocksdb_options_set_hash_skip_list_rep(options, 5000, 4, 4);
1353     rocksdb_options_set_plain_table_factory(options, 4, 10, 0.75, 16);
1354     rocksdb_options_set_allow_concurrent_memtable_write(options, 0);
1355 
1356     db = rocksdb_open(options, dbname, &err);
1357     CheckNoError(err);
1358 
1359     rocksdb_put(db, woptions, "foo1", 4, "foo", 3, &err);
1360     CheckNoError(err);
1361     rocksdb_put(db, woptions, "foo2", 4, "foo", 3, &err);
1362     CheckNoError(err);
1363     rocksdb_put(db, woptions, "foo3", 4, "foo", 3, &err);
1364     CheckNoError(err);
1365     rocksdb_put(db, woptions, "bar1", 4, "bar", 3, &err);
1366     CheckNoError(err);
1367     rocksdb_put(db, woptions, "bar2", 4, "bar", 3, &err);
1368     CheckNoError(err);
1369     rocksdb_put(db, woptions, "bar3", 4, "bar", 3, &err);
1370     CheckNoError(err);
1371 
1372     rocksdb_iterator_t* iter = rocksdb_create_iterator(db, roptions);
1373     CheckCondition(!rocksdb_iter_valid(iter));
1374 
1375     rocksdb_iter_seek(iter, "bar", 3);
1376     rocksdb_iter_get_error(iter, &err);
1377     CheckNoError(err);
1378     CheckCondition(rocksdb_iter_valid(iter));
1379 
1380     CheckIter(iter, "bar1", "bar");
1381     rocksdb_iter_next(iter);
1382     CheckIter(iter, "bar2", "bar");
1383     rocksdb_iter_next(iter);
1384     CheckIter(iter, "bar3", "bar");
1385     rocksdb_iter_get_error(iter, &err);
1386     CheckNoError(err);
1387     rocksdb_iter_destroy(iter);
1388 
1389     rocksdb_readoptions_set_total_order_seek(roptions, 1);
1390     iter = rocksdb_create_iterator(db, roptions);
1391     CheckCondition(!rocksdb_iter_valid(iter));
1392 
1393     rocksdb_iter_seek(iter, "ba", 2);
1394     rocksdb_iter_get_error(iter, &err);
1395     CheckNoError(err);
1396     CheckCondition(rocksdb_iter_valid(iter));
1397     CheckIter(iter, "bar1", "bar");
1398 
1399     rocksdb_iter_destroy(iter);
1400     rocksdb_readoptions_set_total_order_seek(roptions, 0);
1401 
1402     rocksdb_close(db);
1403     rocksdb_destroy_db(options, dbname, &err);
1404   }
1405 
1406   // Check memory usage stats
1407   StartPhase("approximate_memory_usage");
1408   {
1409     // Create database
1410     db = rocksdb_open(options, dbname, &err);
1411     CheckNoError(err);
1412 
1413     rocksdb_memory_consumers_t* consumers;
1414     consumers = rocksdb_memory_consumers_create();
1415     rocksdb_memory_consumers_add_db(consumers, db);
1416     rocksdb_memory_consumers_add_cache(consumers, cache);
1417 
1418     // take memory usage report before write-read operation
1419     rocksdb_memory_usage_t* mu1;
1420     mu1 = rocksdb_approximate_memory_usage_create(consumers, &err);
1421     CheckNoError(err);
1422 
1423     // Put data (this should affect memtables)
1424     rocksdb_put(db, woptions, "memory", 6, "test", 4, &err);
1425     CheckNoError(err);
1426     CheckGet(db, roptions, "memory", "test");
1427 
1428     // take memory usage report after write-read operation
1429     rocksdb_memory_usage_t* mu2;
1430     mu2 = rocksdb_approximate_memory_usage_create(consumers, &err);
1431     CheckNoError(err);
1432 
1433     // amount of memory used within memtables should grow
1434     CheckCondition(rocksdb_approximate_memory_usage_get_mem_table_total(mu2) >=
1435                    rocksdb_approximate_memory_usage_get_mem_table_total(mu1));
1436     CheckCondition(rocksdb_approximate_memory_usage_get_mem_table_unflushed(mu2) >=
1437                    rocksdb_approximate_memory_usage_get_mem_table_unflushed(mu1));
1438 
1439     rocksdb_memory_consumers_destroy(consumers);
1440     rocksdb_approximate_memory_usage_destroy(mu1);
1441     rocksdb_approximate_memory_usage_destroy(mu2);
1442     rocksdb_close(db);
1443     rocksdb_destroy_db(options, dbname, &err);
1444     CheckNoError(err);
1445   }
1446 
1447   StartPhase("cuckoo_options");
1448   {
1449     rocksdb_cuckoo_table_options_t* cuckoo_options;
1450     cuckoo_options = rocksdb_cuckoo_options_create();
1451     rocksdb_cuckoo_options_set_hash_ratio(cuckoo_options, 0.5);
1452     rocksdb_cuckoo_options_set_max_search_depth(cuckoo_options, 200);
1453     rocksdb_cuckoo_options_set_cuckoo_block_size(cuckoo_options, 10);
1454     rocksdb_cuckoo_options_set_identity_as_first_hash(cuckoo_options, 1);
1455     rocksdb_cuckoo_options_set_use_module_hash(cuckoo_options, 0);
1456     rocksdb_options_set_cuckoo_table_factory(options, cuckoo_options);
1457 
1458     db = rocksdb_open(options, dbname, &err);
1459     CheckNoError(err);
1460 
1461     rocksdb_cuckoo_options_destroy(cuckoo_options);
1462   }
1463 
1464   StartPhase("iterate_upper_bound");
1465   {
1466     // Create new empty database
1467     rocksdb_close(db);
1468     rocksdb_destroy_db(options, dbname, &err);
1469     CheckNoError(err);
1470 
1471     rocksdb_options_set_prefix_extractor(options, NULL);
1472     db = rocksdb_open(options, dbname, &err);
1473     CheckNoError(err);
1474 
1475     rocksdb_put(db, woptions, "a",    1, "0",    1, &err); CheckNoError(err);
1476     rocksdb_put(db, woptions, "foo",  3, "bar",  3, &err); CheckNoError(err);
1477     rocksdb_put(db, woptions, "foo1", 4, "bar1", 4, &err); CheckNoError(err);
1478     rocksdb_put(db, woptions, "g1",   2, "0",    1, &err); CheckNoError(err);
1479 
1480     // testing basic case with no iterate_upper_bound and no prefix_extractor
1481     {
1482        rocksdb_readoptions_set_iterate_upper_bound(roptions, NULL, 0);
1483        rocksdb_iterator_t* iter = rocksdb_create_iterator(db, roptions);
1484 
1485        rocksdb_iter_seek(iter, "foo", 3);
1486        CheckCondition(rocksdb_iter_valid(iter));
1487        CheckIter(iter, "foo", "bar");
1488 
1489        rocksdb_iter_next(iter);
1490        CheckCondition(rocksdb_iter_valid(iter));
1491        CheckIter(iter, "foo1", "bar1");
1492 
1493        rocksdb_iter_next(iter);
1494        CheckCondition(rocksdb_iter_valid(iter));
1495        CheckIter(iter, "g1", "0");
1496 
1497        rocksdb_iter_destroy(iter);
1498     }
1499 
1500     // testing iterate_upper_bound and forward iterator
1501     // to make sure it stops at bound
1502     {
1503        // iterate_upper_bound points beyond the last expected entry
1504        rocksdb_readoptions_set_iterate_upper_bound(roptions, "foo2", 4);
1505 
1506        rocksdb_iterator_t* iter = rocksdb_create_iterator(db, roptions);
1507 
1508        rocksdb_iter_seek(iter, "foo", 3);
1509        CheckCondition(rocksdb_iter_valid(iter));
1510        CheckIter(iter, "foo", "bar");
1511 
1512        rocksdb_iter_next(iter);
1513        CheckCondition(rocksdb_iter_valid(iter));
1514        CheckIter(iter, "foo1", "bar1");
1515 
1516        rocksdb_iter_next(iter);
1517        // should stop here...
1518        CheckCondition(!rocksdb_iter_valid(iter));
1519 
1520        rocksdb_iter_destroy(iter);
1521        rocksdb_readoptions_set_iterate_upper_bound(roptions, NULL, 0);
1522     }
1523   }
1524 
1525   StartPhase("transactions");
1526   {
1527     rocksdb_close(db);
1528     rocksdb_destroy_db(options, dbname, &err);
1529     CheckNoError(err);
1530 
1531     // open a TransactionDB
1532     txn_db_options = rocksdb_transactiondb_options_create();
1533     txn_options = rocksdb_transaction_options_create();
1534     rocksdb_options_set_create_if_missing(options, 1);
1535     txn_db = rocksdb_transactiondb_open(options, txn_db_options, dbname, &err);
1536     CheckNoError(err);
1537 
1538     // put outside a transaction
1539     rocksdb_transactiondb_put(txn_db, woptions, "foo", 3, "hello", 5, &err);
1540     CheckNoError(err);
1541     CheckTxnDBGet(txn_db, roptions, "foo", "hello");
1542 
1543     // delete from outside transaction
1544     rocksdb_transactiondb_delete(txn_db, woptions, "foo", 3, &err);
1545     CheckNoError(err);
1546     CheckTxnDBGet(txn_db, roptions, "foo", NULL);
1547 
1548     // write batch into TransactionDB
1549     rocksdb_writebatch_t* wb = rocksdb_writebatch_create();
1550     rocksdb_writebatch_put(wb, "foo", 3, "a", 1);
1551     rocksdb_writebatch_clear(wb);
1552     rocksdb_writebatch_put(wb, "bar", 3, "b", 1);
1553     rocksdb_writebatch_put(wb, "box", 3, "c", 1);
1554     rocksdb_writebatch_delete(wb, "bar", 3);
1555     rocksdb_transactiondb_write(txn_db, woptions, wb, &err);
1556     rocksdb_writebatch_destroy(wb);
1557     CheckTxnDBGet(txn_db, roptions, "box", "c");
1558     CheckNoError(err);
1559 
1560     // begin a transaction
1561     txn = rocksdb_transaction_begin(txn_db, woptions, txn_options, NULL);
1562     // put
1563     rocksdb_transaction_put(txn, "foo", 3, "hello", 5, &err);
1564     CheckNoError(err);
1565     CheckTxnGet(txn, roptions, "foo", "hello");
1566     // delete
1567     rocksdb_transaction_delete(txn, "foo", 3, &err);
1568     CheckNoError(err);
1569     CheckTxnGet(txn, roptions, "foo", NULL);
1570 
1571     rocksdb_transaction_put(txn, "foo", 3, "hello", 5, &err);
1572     CheckNoError(err);
1573 
1574     // read from outside transaction, before commit
1575     CheckTxnDBGet(txn_db, roptions, "foo", NULL);
1576 
1577     // commit
1578     rocksdb_transaction_commit(txn, &err);
1579     CheckNoError(err);
1580 
1581     // read from outside transaction, after commit
1582     CheckTxnDBGet(txn_db, roptions, "foo", "hello");
1583 
1584     // reuse old transaction
1585     txn = rocksdb_transaction_begin(txn_db, woptions, txn_options, txn);
1586 
1587     // snapshot
1588     const rocksdb_snapshot_t* snapshot;
1589     snapshot = rocksdb_transactiondb_create_snapshot(txn_db);
1590     rocksdb_readoptions_set_snapshot(roptions, snapshot);
1591 
1592     rocksdb_transactiondb_put(txn_db, woptions, "foo", 3, "hey", 3,  &err);
1593     CheckNoError(err);
1594 
1595     CheckTxnDBGet(txn_db, roptions, "foo", "hello");
1596     rocksdb_readoptions_set_snapshot(roptions, NULL);
1597     rocksdb_transactiondb_release_snapshot(txn_db, snapshot);
1598     CheckTxnDBGet(txn_db, roptions, "foo", "hey");
1599 
1600     // iterate
1601     rocksdb_transaction_put(txn, "bar", 3, "hi", 2, &err);
1602     rocksdb_iterator_t* iter = rocksdb_transaction_create_iterator(txn, roptions);
1603     CheckCondition(!rocksdb_iter_valid(iter));
1604     rocksdb_iter_seek_to_first(iter);
1605     CheckCondition(rocksdb_iter_valid(iter));
1606     CheckIter(iter, "bar", "hi");
1607     rocksdb_iter_get_error(iter, &err);
1608     CheckNoError(err);
1609     rocksdb_iter_destroy(iter);
1610 
1611     // rollback
1612     rocksdb_transaction_rollback(txn, &err);
1613     CheckNoError(err);
1614     CheckTxnDBGet(txn_db, roptions, "bar", NULL);
1615 
1616     // save point
1617     rocksdb_transaction_put(txn, "foo1", 4, "hi1", 3, &err);
1618     rocksdb_transaction_set_savepoint(txn);
1619     CheckTxnGet(txn, roptions, "foo1", "hi1");
1620     rocksdb_transaction_put(txn, "foo2", 4, "hi2", 3, &err);
1621     CheckTxnGet(txn, roptions, "foo2", "hi2");
1622 
1623     // rollback to savepoint
1624     rocksdb_transaction_rollback_to_savepoint(txn, &err);
1625     CheckNoError(err);
1626     CheckTxnGet(txn, roptions, "foo2", NULL);
1627     CheckTxnGet(txn, roptions, "foo1", "hi1");
1628     CheckTxnDBGet(txn_db, roptions, "foo1", NULL);
1629     CheckTxnDBGet(txn_db, roptions, "foo2", NULL);
1630     rocksdb_transaction_commit(txn, &err);
1631     CheckNoError(err);
1632     CheckTxnDBGet(txn_db, roptions, "foo1", "hi1");
1633     CheckTxnDBGet(txn_db, roptions, "foo2", NULL);
1634 
1635     // Column families.
1636     rocksdb_column_family_handle_t* cfh;
1637     cfh = rocksdb_transactiondb_create_column_family(txn_db, options,
1638                                                      "txn_db_cf", &err);
1639     CheckNoError(err);
1640 
1641     rocksdb_transactiondb_put_cf(txn_db, woptions, cfh, "cf_foo", 6, "cf_hello",
1642                                  8, &err);
1643     CheckNoError(err);
1644     CheckTxnDBGetCF(txn_db, roptions, cfh, "cf_foo", "cf_hello");
1645 
1646     rocksdb_transactiondb_delete_cf(txn_db, woptions, cfh, "cf_foo", 6, &err);
1647     CheckNoError(err);
1648     CheckTxnDBGetCF(txn_db, roptions, cfh, "cf_foo", NULL);
1649 
1650     rocksdb_column_family_handle_destroy(cfh);
1651 
1652     // close and destroy
1653     rocksdb_transaction_destroy(txn);
1654     rocksdb_transactiondb_close(txn_db);
1655     rocksdb_destroy_db(options, dbname, &err);
1656     CheckNoError(err);
1657     rocksdb_transaction_options_destroy(txn_options);
1658     rocksdb_transactiondb_options_destroy(txn_db_options);
1659   }
1660 
1661   StartPhase("optimistic_transactions");
1662   {
1663     rocksdb_options_t* db_options = rocksdb_options_create();
1664     rocksdb_options_set_create_if_missing(db_options, 1);
1665     rocksdb_options_set_allow_concurrent_memtable_write(db_options, 1);
1666     otxn_db = rocksdb_optimistictransactiondb_open(db_options, dbname, &err);
1667     otxn_options = rocksdb_optimistictransaction_options_create();
1668     rocksdb_transaction_t* txn1 = rocksdb_optimistictransaction_begin(
1669         otxn_db, woptions, otxn_options, NULL);
1670     rocksdb_transaction_t* txn2 = rocksdb_optimistictransaction_begin(
1671         otxn_db, woptions, otxn_options, NULL);
1672     rocksdb_transaction_put(txn1, "key", 3, "value", 5, &err);
1673     CheckNoError(err);
1674     rocksdb_transaction_put(txn2, "key1", 4, "value1", 6, &err);
1675     CheckNoError(err);
1676     CheckTxnGet(txn1, roptions, "key", "value");
1677     rocksdb_transaction_commit(txn1, &err);
1678     CheckNoError(err);
1679     rocksdb_transaction_commit(txn2, &err);
1680     CheckNoError(err);
1681     rocksdb_transaction_destroy(txn1);
1682     rocksdb_transaction_destroy(txn2);
1683 
1684     // Check column family
1685     db = rocksdb_optimistictransactiondb_get_base_db(otxn_db);
1686     rocksdb_put(db, woptions, "key", 3, "value", 5, &err);
1687     CheckNoError(err);
1688     rocksdb_column_family_handle_t *cfh1, *cfh2;
1689     cfh1 = rocksdb_create_column_family(db, db_options, "txn_db_cf1", &err);
1690     cfh2 = rocksdb_create_column_family(db, db_options, "txn_db_cf2", &err);
1691     txn = rocksdb_optimistictransaction_begin(otxn_db, woptions, otxn_options,
1692                                               NULL);
1693     rocksdb_transaction_put_cf(txn, cfh1, "key_cf1", 7, "val_cf1", 7, &err);
1694     CheckNoError(err);
1695     rocksdb_transaction_put_cf(txn, cfh2, "key_cf2", 7, "val_cf2", 7, &err);
1696     CheckNoError(err);
1697     rocksdb_transaction_commit(txn, &err);
1698     CheckNoError(err);
1699     txn = rocksdb_optimistictransaction_begin(otxn_db, woptions, otxn_options,
1700                                               txn);
1701     CheckGetCF(db, roptions, cfh1, "key_cf1", "val_cf1");
1702     CheckTxnGetCF(txn, roptions, cfh1, "key_cf1", "val_cf1");
1703 
1704     // Check iterator with column family
1705     rocksdb_transaction_put_cf(txn, cfh1, "key1_cf", 7, "val1_cf", 7, &err);
1706     CheckNoError(err);
1707     rocksdb_iterator_t* iter =
1708         rocksdb_transaction_create_iterator_cf(txn, roptions, cfh1);
1709     CheckCondition(!rocksdb_iter_valid(iter));
1710     rocksdb_iter_seek_to_first(iter);
1711     CheckCondition(rocksdb_iter_valid(iter));
1712     CheckIter(iter, "key1_cf", "val1_cf");
1713     rocksdb_iter_get_error(iter, &err);
1714     CheckNoError(err);
1715     rocksdb_iter_destroy(iter);
1716 
1717     rocksdb_transaction_destroy(txn);
1718     rocksdb_column_family_handle_destroy(cfh1);
1719     rocksdb_column_family_handle_destroy(cfh2);
1720     rocksdb_optimistictransactiondb_close_base_db(db);
1721     rocksdb_optimistictransactiondb_close(otxn_db);
1722 
1723     // Check open optimistic transaction db with column families
1724     size_t cf_len;
1725     char** column_fams =
1726         rocksdb_list_column_families(db_options, dbname, &cf_len, &err);
1727     CheckNoError(err);
1728     CheckEqual("default", column_fams[0], 7);
1729     CheckEqual("txn_db_cf1", column_fams[1], 10);
1730     CheckEqual("txn_db_cf2", column_fams[2], 10);
1731     CheckCondition(cf_len == 3);
1732     rocksdb_list_column_families_destroy(column_fams, cf_len);
1733 
1734     const char* cf_names[3] = {"default", "txn_db_cf1", "txn_db_cf2"};
1735     rocksdb_options_t* cf_options = rocksdb_options_create();
1736     const rocksdb_options_t* cf_opts[3] = {cf_options, cf_options, cf_options};
1737 
1738     rocksdb_options_set_error_if_exists(cf_options, 0);
1739     rocksdb_column_family_handle_t* cf_handles[3];
1740     otxn_db = rocksdb_optimistictransactiondb_open_column_families(
1741         db_options, dbname, 3, cf_names, cf_opts, cf_handles, &err);
1742     CheckNoError(err);
1743     rocksdb_transaction_t* txn_cf = rocksdb_optimistictransaction_begin(
1744         otxn_db, woptions, otxn_options, NULL);
1745     CheckTxnGetCF(txn_cf, roptions, cf_handles[0], "key", "value");
1746     CheckTxnGetCF(txn_cf, roptions, cf_handles[1], "key_cf1", "val_cf1");
1747     CheckTxnGetCF(txn_cf, roptions, cf_handles[2], "key_cf2", "val_cf2");
1748     rocksdb_transaction_destroy(txn_cf);
1749     rocksdb_options_destroy(cf_options);
1750     rocksdb_column_family_handle_destroy(cf_handles[0]);
1751     rocksdb_column_family_handle_destroy(cf_handles[1]);
1752     rocksdb_column_family_handle_destroy(cf_handles[2]);
1753     rocksdb_optimistictransactiondb_close(otxn_db);
1754     rocksdb_destroy_db(db_options, dbname, &err);
1755     rocksdb_options_destroy(db_options);
1756     rocksdb_optimistictransaction_options_destroy(otxn_options);
1757     CheckNoError(err);
1758   }
1759 
1760   // Simple sanity check that setting memtable rep works.
1761   StartPhase("memtable_reps");
1762   {
1763     // Create database with vector memtable.
1764     rocksdb_options_set_memtable_vector_rep(options);
1765     db = rocksdb_open(options, dbname, &err);
1766     CheckNoError(err);
1767 
1768     // Create database with hash skiplist memtable.
1769     rocksdb_close(db);
1770     rocksdb_destroy_db(options, dbname, &err);
1771     CheckNoError(err);
1772 
1773     rocksdb_options_set_hash_skip_list_rep(options, 5000, 4, 4);
1774     db = rocksdb_open(options, dbname, &err);
1775     CheckNoError(err);
1776   }
1777 
1778   // Check that secondary instance works.
1779   StartPhase("open_as_secondary");
1780   {
1781     rocksdb_close(db);
1782     rocksdb_destroy_db(options, dbname, &err);
1783 
1784     rocksdb_options_t* db_options = rocksdb_options_create();
1785     rocksdb_options_set_create_if_missing(db_options, 1);
1786     db = rocksdb_open(db_options, dbname, &err);
1787     CheckNoError(err);
1788     rocksdb_t* db1;
1789     rocksdb_options_t* opts = rocksdb_options_create();
1790     rocksdb_options_set_max_open_files(opts, -1);
1791     rocksdb_options_set_create_if_missing(opts, 1);
1792     snprintf(secondary_path, sizeof(secondary_path),
1793              "%s/rocksdb_c_test_secondary-%d", GetTempDir(), ((int)geteuid()));
1794     db1 = rocksdb_open_as_secondary(opts, dbname, secondary_path, &err);
1795     CheckNoError(err);
1796 
1797     rocksdb_writeoptions_set_sync(woptions, 0);
1798     rocksdb_writeoptions_disable_WAL(woptions, 1);
1799     rocksdb_put(db, woptions, "key0", 4, "value0", 6, &err);
1800     CheckNoError(err);
1801     rocksdb_flushoptions_t* flush_opts = rocksdb_flushoptions_create();
1802     rocksdb_flushoptions_set_wait(flush_opts, 1);
1803     rocksdb_flush(db, flush_opts, &err);
1804     CheckNoError(err);
1805     rocksdb_try_catch_up_with_primary(db1, &err);
1806     CheckNoError(err);
1807     rocksdb_readoptions_t* ropts = rocksdb_readoptions_create();
1808     rocksdb_readoptions_set_verify_checksums(ropts, 1);
1809     rocksdb_readoptions_set_snapshot(ropts, NULL);
1810     CheckGet(db, ropts, "key0", "value0");
1811     CheckGet(db1, ropts, "key0", "value0");
1812 
1813     rocksdb_writeoptions_disable_WAL(woptions, 0);
1814     rocksdb_put(db, woptions, "key1", 4, "value1", 6, &err);
1815     CheckNoError(err);
1816     rocksdb_try_catch_up_with_primary(db1, &err);
1817     CheckNoError(err);
1818     CheckGet(db1, ropts, "key0", "value0");
1819     CheckGet(db1, ropts, "key1", "value1");
1820 
1821     rocksdb_close(db1);
1822     rocksdb_destroy_db(opts, secondary_path, &err);
1823     CheckNoError(err);
1824 
1825     rocksdb_options_destroy(db_options);
1826     rocksdb_options_destroy(opts);
1827     rocksdb_readoptions_destroy(ropts);
1828     rocksdb_flushoptions_destroy(flush_opts);
1829   }
1830 
1831   // Simple sanity check that options setting db_paths work.
1832   StartPhase("open_db_paths");
1833   {
1834     rocksdb_close(db);
1835     rocksdb_destroy_db(options, dbname, &err);
1836 
1837     const rocksdb_dbpath_t* paths[1] = {dbpath};
1838     rocksdb_options_set_db_paths(options, paths, 1);
1839     db = rocksdb_open(options, dbname, &err);
1840     CheckNoError(err);
1841   }
1842 
1843   StartPhase("cleanup");
1844   rocksdb_close(db);
1845   rocksdb_options_destroy(options);
1846   rocksdb_block_based_options_destroy(table_options);
1847   rocksdb_readoptions_destroy(roptions);
1848   rocksdb_writeoptions_destroy(woptions);
1849   rocksdb_compactoptions_destroy(coptions);
1850   rocksdb_cache_destroy(cache);
1851   rocksdb_comparator_destroy(cmp);
1852   rocksdb_dbpath_destroy(dbpath);
1853   rocksdb_env_destroy(env);
1854 
1855   fprintf(stderr, "PASS\n");
1856   return 0;
1857 }
1858 
1859 #else
1860 
main()1861 int main() {
1862   fprintf(stderr, "SKIPPED\n");
1863   return 0;
1864 }
1865 
1866 #endif  // !ROCKSDB_LITE
1867