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