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 
StartPhase(const char * name)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 
CheckEqual(const char * expected,const char * v,size_t n)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 
Free(char ** ptr)47 static void Free(char** ptr) {
48   if (*ptr) {
49     free(*ptr);
50     *ptr = NULL;
51   }
52 }
53 
CheckGet(leveldb_t * db,const leveldb_readoptions_t * options,const char * key,const char * expected)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 
CheckIter(leveldb_iterator_t * iter,const char * key,const char * val)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()
CheckPut(void * ptr,const char * k,size_t klen,const char * v,size_t vlen)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()
CheckDel(void * ptr,const char * k,size_t klen)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 
CmpDestroy(void * arg)105 static void CmpDestroy(void* arg) { }
106 
CmpCompare(void * arg,const char * a,size_t alen,const char * b,size_t blen)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 
CmpName(void * arg)118 static const char* CmpName(void* arg) {
119   return "foo";
120 }
121 
122 // Custom filter policy
123 static unsigned char fake_filter_result = 1;
FilterDestroy(void * arg)124 static void FilterDestroy(void* arg) { }
FilterName(void * arg)125 static const char* FilterName(void* arg) {
126   return "TestFilter";
127 }
FilterCreate(void * arg,const char * const * key_array,const size_t * key_length_array,int num_keys,size_t * filter_length)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 }
FilterKeyMatch(void * arg,const char * key,size_t length,const char * filter,size_t filter_length)138 unsigned char FilterKeyMatch(
139     void* arg,
140     const char* key, size_t length,
141     const char* filter, size_t filter_length) {
142   CheckCondition(filter_length == 4);
143   CheckCondition(memcmp(filter, "fake", 4) == 0);
144   return fake_filter_result;
145 }
146 
main(int argc,char ** argv)147 int main(int argc, char** argv) {
148   leveldb_t* db;
149   leveldb_comparator_t* cmp;
150   leveldb_cache_t* cache;
151   leveldb_env_t* env;
152   leveldb_options_t* options;
153   leveldb_readoptions_t* roptions;
154   leveldb_writeoptions_t* woptions;
155   char* dbname;
156   char* err = NULL;
157   int run = -1;
158 
159   CheckCondition(leveldb_major_version() >= 1);
160   CheckCondition(leveldb_minor_version() >= 1);
161 
162   StartPhase("create_objects");
163   cmp = leveldb_comparator_create(NULL, CmpDestroy, CmpCompare, CmpName);
164   env = leveldb_create_default_env();
165   cache = leveldb_cache_create_lru(100000);
166   dbname = leveldb_env_get_test_directory(env);
167   CheckCondition(dbname != NULL);
168 
169   options = leveldb_options_create();
170   leveldb_options_set_comparator(options, cmp);
171   leveldb_options_set_error_if_exists(options, 1);
172   leveldb_options_set_cache(options, cache);
173   leveldb_options_set_env(options, env);
174   leveldb_options_set_info_log(options, NULL);
175   leveldb_options_set_write_buffer_size(options, 100000);
176   leveldb_options_set_paranoid_checks(options, 1);
177   leveldb_options_set_max_open_files(options, 10);
178   leveldb_options_set_block_size(options, 1024);
179   leveldb_options_set_block_restart_interval(options, 8);
180   leveldb_options_set_max_file_size(options, 3 << 20);
181   leveldb_options_set_compression(options, leveldb_no_compression);
182 
183   roptions = leveldb_readoptions_create();
184   leveldb_readoptions_set_verify_checksums(roptions, 1);
185   leveldb_readoptions_set_fill_cache(roptions, 0);
186 
187   woptions = leveldb_writeoptions_create();
188   leveldb_writeoptions_set_sync(woptions, 1);
189 
190   StartPhase("destroy");
191   leveldb_destroy_db(options, dbname, &err);
192   Free(&err);
193 
194   StartPhase("open_error");
195   db = leveldb_open(options, dbname, &err);
196   CheckCondition(err != NULL);
197   Free(&err);
198 
199   StartPhase("leveldb_free");
200   db = leveldb_open(options, dbname, &err);
201   CheckCondition(err != NULL);
202   leveldb_free(err);
203   err = NULL;
204 
205   StartPhase("open");
206   leveldb_options_set_create_if_missing(options, 1);
207   db = leveldb_open(options, dbname, &err);
208   CheckNoError(err);
209   CheckGet(db, roptions, "foo", NULL);
210 
211   StartPhase("put");
212   leveldb_put(db, woptions, "foo", 3, "hello", 5, &err);
213   CheckNoError(err);
214   CheckGet(db, roptions, "foo", "hello");
215 
216   StartPhase("compactall");
217   leveldb_compact_range(db, NULL, 0, NULL, 0);
218   CheckGet(db, roptions, "foo", "hello");
219 
220   StartPhase("compactrange");
221   leveldb_compact_range(db, "a", 1, "z", 1);
222   CheckGet(db, roptions, "foo", "hello");
223 
224   StartPhase("writebatch");
225   {
226     leveldb_writebatch_t* wb = leveldb_writebatch_create();
227     leveldb_writebatch_put(wb, "foo", 3, "a", 1);
228     leveldb_writebatch_clear(wb);
229     leveldb_writebatch_put(wb, "bar", 3, "b", 1);
230     leveldb_writebatch_put(wb, "box", 3, "c", 1);
231 
232     leveldb_writebatch_t* wb2 = leveldb_writebatch_create();
233     leveldb_writebatch_delete(wb2, "bar", 3);
234     leveldb_writebatch_append(wb, wb2);
235     leveldb_writebatch_destroy(wb2);
236 
237     leveldb_write(db, woptions, wb, &err);
238     CheckNoError(err);
239     CheckGet(db, roptions, "foo", "hello");
240     CheckGet(db, roptions, "bar", NULL);
241     CheckGet(db, roptions, "box", "c");
242 
243     int pos = 0;
244     leveldb_writebatch_iterate(wb, &pos, CheckPut, CheckDel);
245     CheckCondition(pos == 3);
246     leveldb_writebatch_destroy(wb);
247   }
248 
249   StartPhase("iter");
250   {
251     leveldb_iterator_t* iter = leveldb_create_iterator(db, roptions);
252     CheckCondition(!leveldb_iter_valid(iter));
253     leveldb_iter_seek_to_first(iter);
254     CheckCondition(leveldb_iter_valid(iter));
255     CheckIter(iter, "box", "c");
256     leveldb_iter_next(iter);
257     CheckIter(iter, "foo", "hello");
258     leveldb_iter_prev(iter);
259     CheckIter(iter, "box", "c");
260     leveldb_iter_prev(iter);
261     CheckCondition(!leveldb_iter_valid(iter));
262     leveldb_iter_seek_to_last(iter);
263     CheckIter(iter, "foo", "hello");
264     leveldb_iter_seek(iter, "b", 1);
265     CheckIter(iter, "box", "c");
266     leveldb_iter_get_error(iter, &err);
267     CheckNoError(err);
268     leveldb_iter_destroy(iter);
269   }
270 
271   StartPhase("approximate_sizes");
272   {
273     int i;
274     int n = 20000;
275     char keybuf[100];
276     char valbuf[100];
277     uint64_t sizes[2];
278     const char* start[2] = { "a", "k00000000000000010000" };
279     size_t start_len[2] = { 1, 21 };
280     const char* limit[2] = { "k00000000000000010000", "z" };
281     size_t limit_len[2] = { 21, 1 };
282     leveldb_writeoptions_set_sync(woptions, 0);
283     for (i = 0; i < n; i++) {
284       snprintf(keybuf, sizeof(keybuf), "k%020d", i);
285       snprintf(valbuf, sizeof(valbuf), "v%020d", i);
286       leveldb_put(db, woptions, keybuf, strlen(keybuf), valbuf, strlen(valbuf),
287                   &err);
288       CheckNoError(err);
289     }
290     leveldb_approximate_sizes(db, 2, start, start_len, limit, limit_len, sizes);
291     CheckCondition(sizes[0] > 0);
292     CheckCondition(sizes[1] > 0);
293   }
294 
295   StartPhase("property");
296   {
297     char* prop = leveldb_property_value(db, "nosuchprop");
298     CheckCondition(prop == NULL);
299     prop = leveldb_property_value(db, "leveldb.stats");
300     CheckCondition(prop != NULL);
301     Free(&prop);
302   }
303 
304   StartPhase("snapshot");
305   {
306     const leveldb_snapshot_t* snap;
307     snap = leveldb_create_snapshot(db);
308     leveldb_delete(db, woptions, "foo", 3, &err);
309     CheckNoError(err);
310     leveldb_readoptions_set_snapshot(roptions, snap);
311     CheckGet(db, roptions, "foo", "hello");
312     leveldb_readoptions_set_snapshot(roptions, NULL);
313     CheckGet(db, roptions, "foo", NULL);
314     leveldb_release_snapshot(db, snap);
315   }
316 
317   StartPhase("repair");
318   {
319     leveldb_close(db);
320     leveldb_options_set_create_if_missing(options, 0);
321     leveldb_options_set_error_if_exists(options, 0);
322     leveldb_repair_db(options, dbname, &err);
323     CheckNoError(err);
324     db = leveldb_open(options, dbname, &err);
325     CheckNoError(err);
326     CheckGet(db, roptions, "foo", NULL);
327     CheckGet(db, roptions, "bar", NULL);
328     CheckGet(db, roptions, "box", "c");
329     leveldb_options_set_create_if_missing(options, 1);
330     leveldb_options_set_error_if_exists(options, 1);
331   }
332 
333   StartPhase("filter");
334   for (run = 0; run < 2; run++) {
335     // First run uses custom filter, second run uses bloom filter
336     CheckNoError(err);
337     leveldb_filterpolicy_t* policy;
338     if (run == 0) {
339       policy = leveldb_filterpolicy_create(
340           NULL, FilterDestroy, FilterCreate, FilterKeyMatch, FilterName);
341     } else {
342       policy = leveldb_filterpolicy_create_bloom(10);
343     }
344 
345     // Create new database
346     leveldb_close(db);
347     leveldb_destroy_db(options, dbname, &err);
348     leveldb_options_set_filter_policy(options, policy);
349     db = leveldb_open(options, dbname, &err);
350     CheckNoError(err);
351     leveldb_put(db, woptions, "foo", 3, "foovalue", 8, &err);
352     CheckNoError(err);
353     leveldb_put(db, woptions, "bar", 3, "barvalue", 8, &err);
354     CheckNoError(err);
355     leveldb_compact_range(db, NULL, 0, NULL, 0);
356 
357     fake_filter_result = 1;
358     CheckGet(db, roptions, "foo", "foovalue");
359     CheckGet(db, roptions, "bar", "barvalue");
360     if (phase == 0) {
361       // Must not find value when custom filter returns false
362       fake_filter_result = 0;
363       CheckGet(db, roptions, "foo", NULL);
364       CheckGet(db, roptions, "bar", NULL);
365       fake_filter_result = 1;
366 
367       CheckGet(db, roptions, "foo", "foovalue");
368       CheckGet(db, roptions, "bar", "barvalue");
369     }
370     leveldb_options_set_filter_policy(options, NULL);
371     leveldb_filterpolicy_destroy(policy);
372   }
373 
374   StartPhase("cleanup");
375   leveldb_close(db);
376   leveldb_options_destroy(options);
377   leveldb_readoptions_destroy(roptions);
378   leveldb_writeoptions_destroy(woptions);
379   leveldb_free(dbname);
380   leveldb_cache_destroy(cache);
381   leveldb_comparator_destroy(cmp);
382   leveldb_env_destroy(env);
383 
384   fprintf(stderr, "PASS\n");
385   return 0;
386 }
387