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 | } |
385 | |