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