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
StartPhase(const char * name)17 static void StartPhase(const char* name) {
18 fprintf(stderr, "=== Test %s\n", name);
19 phase = name;
20 }
21
GetTempDir(void)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
CheckEqual(const char * expected,const char * v,size_t n)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
Free(char ** ptr)57 static void Free(char** ptr) {
58 if (*ptr) {
59 free(*ptr);
60 *ptr = NULL;
61 }
62 }
63
CheckGet(leveldb_t * db,const leveldb_readoptions_t * options,const char * key,const char * expected)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
CheckIter(leveldb_iterator_t * iter,const char * key,const char * val)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()
CheckPut(void * ptr,const char * k,size_t klen,const char * v,size_t vlen)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()
CheckDel(void * ptr,const char * k,size_t klen)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
CmpDestroy(void * arg)115 static void CmpDestroy(void* arg) { }
116
CmpCompare(void * arg,const char * a,size_t alen,const char * b,size_t blen)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
CmpName(void * arg)128 static const char* CmpName(void* arg) {
129 return "foo";
130 }
131
132 // Custom filter policy
133 static unsigned char fake_filter_result = 1;
FilterDestroy(void * arg)134 static void FilterDestroy(void* arg) { }
FilterName(void * arg)135 static const char* FilterName(void* arg) {
136 return "TestFilter";
137 }
FilterCreate(void * arg,const char * const * key_array,const size_t * key_length_array,int num_keys,size_t * filter_length)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 }
FilterKeyMatch(void * arg,const char * key,size_t length,const char * filter,size_t filter_length)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
main(int argc,char ** argv)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