Home | History | Annotate | Download | only in db
      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 
      5 #include "leveldb/c.h"
      6 
      7 #include <stddef.h>
      8 #include <stdio.h>
      9 #include <stdlib.h>
     10 #include <string.h>
     11 #include <sys/types.h>
     12 #include <unistd.h>
     13 
     14 const char* phase = "";
     15 static char dbname[200];
     16 
     17 static void StartPhase(const char* name) {
     18   fprintf(stderr, "=== Test %s\n", name);
     19   phase = name;
     20 }
     21 
     22 static const char* GetTempDir(void) {
     23     const char* ret = getenv("TEST_TMPDIR");
     24     if (ret == NULL || ret[0] == '\0')
     25         ret = "/tmp";
     26     return ret;
     27 }
     28 
     29 #define CheckNoError(err)                                               \
     30   if ((err) != NULL) {                                                  \
     31     fprintf(stderr, "%s:%d: %s: %s\n", __FILE__, __LINE__, phase, (err)); \
     32     abort();                                                            \
     33   }
     34 
     35 #define CheckCondition(cond)                                            \
     36   if (!(cond)) {                                                        \
     37     fprintf(stderr, "%s:%d: %s: %s\n", __FILE__, __LINE__, phase, #cond); \
     38     abort();                                                            \
     39   }
     40 
     41 static void CheckEqual(const char* expected, const char* v, size_t n) {
     42   if (expected == NULL && v == NULL) {
     43     // ok
     44   } else if (expected != NULL && v != NULL && n == strlen(expected) &&
     45              memcmp(expected, v, n) == 0) {
     46     // ok
     47     return;
     48   } else {
     49     fprintf(stderr, "%s: expected '%s', got '%s'\n",
     50             phase,
     51             (expected ? expected : "(null)"),
     52             (v ? v : "(null"));
     53     abort();
     54   }
     55 }
     56 
     57 static void Free(char** ptr) {
     58   if (*ptr) {
     59     free(*ptr);
     60     *ptr = NULL;
     61   }
     62 }
     63 
     64 static void CheckGet(
     65     leveldb_t* db,
     66     const leveldb_readoptions_t* options,
     67     const char* key,
     68     const char* expected) {
     69   char* err = NULL;
     70   size_t val_len;
     71   char* val;
     72   val = leveldb_get(db, options, key, strlen(key), &val_len, &err);
     73   CheckNoError(err);
     74   CheckEqual(expected, val, val_len);
     75   Free(&val);
     76 }
     77 
     78 static void CheckIter(leveldb_iterator_t* iter,
     79                       const char* key, const char* val) {
     80   size_t len;
     81   const char* str;
     82   str = leveldb_iter_key(iter, &len);
     83   CheckEqual(key, str, len);
     84   str = leveldb_iter_value(iter, &len);
     85   CheckEqual(val, str, len);
     86 }
     87 
     88 // Callback from leveldb_writebatch_iterate()
     89 static void CheckPut(void* ptr,
     90                      const char* k, size_t klen,
     91                      const char* v, size_t vlen) {
     92   int* state = (int*) ptr;
     93   CheckCondition(*state < 2);
     94   switch (*state) {
     95     case 0:
     96       CheckEqual("bar", k, klen);
     97       CheckEqual("b", v, vlen);
     98       break;
     99     case 1:
    100       CheckEqual("box", k, klen);
    101       CheckEqual("c", v, vlen);
    102       break;
    103   }
    104   (*state)++;
    105 }
    106 
    107 // Callback from leveldb_writebatch_iterate()
    108 static void CheckDel(void* ptr, const char* k, size_t klen) {
    109   int* state = (int*) ptr;
    110   CheckCondition(*state == 2);
    111   CheckEqual("bar", k, klen);
    112   (*state)++;
    113 }
    114 
    115 static void CmpDestroy(void* arg) { }
    116 
    117 static int CmpCompare(void* arg, const char* a, size_t alen,
    118                       const char* b, size_t blen) {
    119   int n = (alen < blen) ? alen : blen;
    120   int r = memcmp(a, b, n);
    121   if (r == 0) {
    122     if (alen < blen) r = -1;
    123     else if (alen > blen) r = +1;
    124   }
    125   return r;
    126 }
    127 
    128 static const char* CmpName(void* arg) {
    129   return "foo";
    130 }
    131 
    132 // Custom filter policy
    133 static unsigned char fake_filter_result = 1;
    134 static void FilterDestroy(void* arg) { }
    135 static const char* FilterName(void* arg) {
    136   return "TestFilter";
    137 }
    138 static char* FilterCreate(
    139     void* arg,
    140     const char* const* key_array, const size_t* key_length_array,
    141     int num_keys,
    142     size_t* filter_length) {
    143   *filter_length = 4;
    144   char* result = malloc(4);
    145   memcpy(result, "fake", 4);
    146   return result;
    147 }
    148 unsigned char FilterKeyMatch(
    149     void* arg,
    150     const char* key, size_t length,
    151     const char* filter, size_t filter_length) {
    152   CheckCondition(filter_length == 4);
    153   CheckCondition(memcmp(filter, "fake", 4) == 0);
    154   return fake_filter_result;
    155 }
    156 
    157 int main(int argc, char** argv) {
    158   leveldb_t* db;
    159   leveldb_comparator_t* cmp;
    160   leveldb_cache_t* cache;
    161   leveldb_env_t* env;
    162   leveldb_options_t* options;
    163   leveldb_readoptions_t* roptions;
    164   leveldb_writeoptions_t* woptions;
    165   char* err = NULL;
    166   int run = -1;
    167 
    168   CheckCondition(leveldb_major_version() >= 1);
    169   CheckCondition(leveldb_minor_version() >= 1);
    170 
    171   snprintf(dbname, sizeof(dbname),
    172            "%s/leveldb_c_test-%d",
    173            GetTempDir(),
    174            ((int) geteuid()));
    175 
    176   StartPhase("create_objects");
    177   cmp = leveldb_comparator_create(NULL, CmpDestroy, CmpCompare, CmpName);
    178   env = leveldb_create_default_env();
    179   cache = leveldb_cache_create_lru(100000);
    180 
    181   options = leveldb_options_create();
    182   leveldb_options_set_comparator(options, cmp);
    183   leveldb_options_set_error_if_exists(options, 1);
    184   leveldb_options_set_cache(options, cache);
    185   leveldb_options_set_env(options, env);
    186   leveldb_options_set_info_log(options, NULL);
    187   leveldb_options_set_write_buffer_size(options, 100000);
    188   leveldb_options_set_paranoid_checks(options, 1);
    189   leveldb_options_set_max_open_files(options, 10);
    190   leveldb_options_set_block_size(options, 1024);
    191   leveldb_options_set_block_restart_interval(options, 8);
    192   leveldb_options_set_compression(options, leveldb_no_compression);
    193 
    194   roptions = leveldb_readoptions_create();
    195   leveldb_readoptions_set_verify_checksums(roptions, 1);
    196   leveldb_readoptions_set_fill_cache(roptions, 0);
    197 
    198   woptions = leveldb_writeoptions_create();
    199   leveldb_writeoptions_set_sync(woptions, 1);
    200 
    201   StartPhase("destroy");
    202   leveldb_destroy_db(options, dbname, &err);
    203   Free(&err);
    204 
    205   StartPhase("open_error");
    206   db = leveldb_open(options, dbname, &err);
    207   CheckCondition(err != NULL);
    208   Free(&err);
    209 
    210   StartPhase("leveldb_free");
    211   db = leveldb_open(options, dbname, &err);
    212   CheckCondition(err != NULL);
    213   leveldb_free(err);
    214   err = NULL;
    215 
    216   StartPhase("open");
    217   leveldb_options_set_create_if_missing(options, 1);
    218   db = leveldb_open(options, dbname, &err);
    219   CheckNoError(err);
    220   CheckGet(db, roptions, "foo", NULL);
    221 
    222   StartPhase("put");
    223   leveldb_put(db, woptions, "foo", 3, "hello", 5, &err);
    224   CheckNoError(err);
    225   CheckGet(db, roptions, "foo", "hello");
    226 
    227   StartPhase("compactall");
    228   leveldb_compact_range(db, NULL, 0, NULL, 0);
    229   CheckGet(db, roptions, "foo", "hello");
    230 
    231   StartPhase("compactrange");
    232   leveldb_compact_range(db, "a", 1, "z", 1);
    233   CheckGet(db, roptions, "foo", "hello");
    234 
    235   StartPhase("writebatch");
    236   {
    237     leveldb_writebatch_t* wb = leveldb_writebatch_create();
    238     leveldb_writebatch_put(wb, "foo", 3, "a", 1);
    239     leveldb_writebatch_clear(wb);
    240     leveldb_writebatch_put(wb, "bar", 3, "b", 1);
    241     leveldb_writebatch_put(wb, "box", 3, "c", 1);
    242     leveldb_writebatch_delete(wb, "bar", 3);
    243     leveldb_write(db, woptions, wb, &err);
    244     CheckNoError(err);
    245     CheckGet(db, roptions, "foo", "hello");
    246     CheckGet(db, roptions, "bar", NULL);
    247     CheckGet(db, roptions, "box", "c");
    248     int pos = 0;
    249     leveldb_writebatch_iterate(wb, &pos, CheckPut, CheckDel);
    250     CheckCondition(pos == 3);
    251     leveldb_writebatch_destroy(wb);
    252   }
    253 
    254   StartPhase("iter");
    255   {
    256     leveldb_iterator_t* iter = leveldb_create_iterator(db, roptions);
    257     CheckCondition(!leveldb_iter_valid(iter));
    258     leveldb_iter_seek_to_first(iter);
    259     CheckCondition(leveldb_iter_valid(iter));
    260     CheckIter(iter, "box", "c");
    261     leveldb_iter_next(iter);
    262     CheckIter(iter, "foo", "hello");
    263     leveldb_iter_prev(iter);
    264     CheckIter(iter, "box", "c");
    265     leveldb_iter_prev(iter);
    266     CheckCondition(!leveldb_iter_valid(iter));
    267     leveldb_iter_seek_to_last(iter);
    268     CheckIter(iter, "foo", "hello");
    269     leveldb_iter_seek(iter, "b", 1);
    270     CheckIter(iter, "box", "c");
    271     leveldb_iter_get_error(iter, &err);
    272     CheckNoError(err);
    273     leveldb_iter_destroy(iter);
    274   }
    275 
    276   StartPhase("approximate_sizes");
    277   {
    278     int i;
    279     int n = 20000;
    280     char keybuf[100];
    281     char valbuf[100];
    282     uint64_t sizes[2];
    283     const char* start[2] = { "a", "k00000000000000010000" };
    284     size_t start_len[2] = { 1, 21 };
    285     const char* limit[2] = { "k00000000000000010000", "z" };
    286     size_t limit_len[2] = { 21, 1 };
    287     leveldb_writeoptions_set_sync(woptions, 0);
    288     for (i = 0; i < n; i++) {
    289       snprintf(keybuf, sizeof(keybuf), "k%020d", i);
    290       snprintf(valbuf, sizeof(valbuf), "v%020d", i);
    291       leveldb_put(db, woptions, keybuf, strlen(keybuf), valbuf, strlen(valbuf),
    292                   &err);
    293       CheckNoError(err);
    294     }
    295     leveldb_approximate_sizes(db, 2, start, start_len, limit, limit_len, sizes);
    296     CheckCondition(sizes[0] > 0);
    297     CheckCondition(sizes[1] > 0);
    298   }
    299 
    300   StartPhase("property");
    301   {
    302     char* prop = leveldb_property_value(db, "nosuchprop");
    303     CheckCondition(prop == NULL);
    304     prop = leveldb_property_value(db, "leveldb.stats");
    305     CheckCondition(prop != NULL);
    306     Free(&prop);
    307   }
    308 
    309   StartPhase("snapshot");
    310   {
    311     const leveldb_snapshot_t* snap;
    312     snap = leveldb_create_snapshot(db);
    313     leveldb_delete(db, woptions, "foo", 3, &err);
    314     CheckNoError(err);
    315     leveldb_readoptions_set_snapshot(roptions, snap);
    316     CheckGet(db, roptions, "foo", "hello");
    317     leveldb_readoptions_set_snapshot(roptions, NULL);
    318     CheckGet(db, roptions, "foo", NULL);
    319     leveldb_release_snapshot(db, snap);
    320   }
    321 
    322   StartPhase("repair");
    323   {
    324     leveldb_close(db);
    325     leveldb_options_set_create_if_missing(options, 0);
    326     leveldb_options_set_error_if_exists(options, 0);
    327     leveldb_repair_db(options, dbname, &err);
    328     CheckNoError(err);
    329     db = leveldb_open(options, dbname, &err);
    330     CheckNoError(err);
    331     CheckGet(db, roptions, "foo", NULL);
    332     CheckGet(db, roptions, "bar", NULL);
    333     CheckGet(db, roptions, "box", "c");
    334     leveldb_options_set_create_if_missing(options, 1);
    335     leveldb_options_set_error_if_exists(options, 1);
    336   }
    337 
    338   StartPhase("filter");
    339   for (run = 0; run < 2; run++) {
    340     // First run uses custom filter, second run uses bloom filter
    341     CheckNoError(err);
    342     leveldb_filterpolicy_t* policy;
    343     if (run == 0) {
    344       policy = leveldb_filterpolicy_create(
    345           NULL, FilterDestroy, FilterCreate, FilterKeyMatch, FilterName);
    346     } else {
    347       policy = leveldb_filterpolicy_create_bloom(10);
    348     }
    349 
    350     // Create new database
    351     leveldb_close(db);
    352     leveldb_destroy_db(options, dbname, &err);
    353     leveldb_options_set_filter_policy(options, policy);
    354     db = leveldb_open(options, dbname, &err);
    355     CheckNoError(err);
    356     leveldb_put(db, woptions, "foo", 3, "foovalue", 8, &err);
    357     CheckNoError(err);
    358     leveldb_put(db, woptions, "bar", 3, "barvalue", 8, &err);
    359     CheckNoError(err);
    360     leveldb_compact_range(db, NULL, 0, NULL, 0);
    361 
    362     fake_filter_result = 1;
    363     CheckGet(db, roptions, "foo", "foovalue");
    364     CheckGet(db, roptions, "bar", "barvalue");
    365     if (phase == 0) {
    366       // Must not find value when custom filter returns false
    367       fake_filter_result = 0;
    368       CheckGet(db, roptions, "foo", NULL);
    369       CheckGet(db, roptions, "bar", NULL);
    370       fake_filter_result = 1;
    371 
    372       CheckGet(db, roptions, "foo", "foovalue");
    373       CheckGet(db, roptions, "bar", "barvalue");
    374     }
    375     leveldb_options_set_filter_policy(options, NULL);
    376     leveldb_filterpolicy_destroy(policy);
    377   }
    378 
    379   StartPhase("cleanup");
    380   leveldb_close(db);
    381   leveldb_options_destroy(options);
    382   leveldb_readoptions_destroy(roptions);
    383   leveldb_writeoptions_destroy(woptions);
    384   leveldb_cache_destroy(cache);
    385   leveldb_comparator_destroy(cmp);
    386   leveldb_env_destroy(env);
    387 
    388   fprintf(stderr, "PASS\n");
    389   return 0;
    390 }
    391