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