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
14const char* phase = "";
15static char dbname[200];
16
17static void StartPhase(const char* name) {
18  fprintf(stderr, "=== Test %s\n", name);
19  phase = name;
20}
21
22static 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
41static 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
57static void Free(char** ptr) {
58  if (*ptr) {
59    free(*ptr);
60    *ptr = NULL;
61  }
62}
63
64static 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
78static 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()
89static 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()
108static 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
115static void CmpDestroy(void* arg) { }
116
117static 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
128static const char* CmpName(void* arg) {
129  return "foo";
130}
131
132// Custom filter policy
133static unsigned char fake_filter_result = 1;
134static void FilterDestroy(void* arg) { }
135static const char* FilterName(void* arg) {
136  return "TestFilter";
137}
138static 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}
148unsigned 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
157int 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