c_test.c 33.6 KB
Newer Older
1 2 3 4
/* Copyright (c) 2011 The LevelDB Authors. All rights reserved.
   Use of this source code is governed by a BSD-style license that can be
   found in the LICENSE file. See the AUTHORS file for names of contributors. */

Y
Yueh-Hsuan Chiang 已提交
5 6
#ifndef ROCKSDB_LITE  // Lite does not support C API

7
#include "rocksdb/c.h"
8 9 10 11 12 13 14

#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
15
#include <inttypes.h>
16 17 18

const char* phase = "";
static char dbname[200];
19
static char dbbackupname[200];
20 21 22 23 24 25

static void StartPhase(const char* name) {
  fprintf(stderr, "=== Test %s\n", name);
  phase = name;
}

H
heyongqiang 已提交
26 27 28 29 30 31 32
static const char* GetTempDir(void) {
    const char* ret = getenv("TEST_TMPDIR");
    if (ret == NULL || ret[0] == '\0')
        ret = "/tmp";
    return ret;
}

33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
#define CheckNoError(err)                                               \
  if ((err) != NULL) {                                                  \
    fprintf(stderr, "%s:%d: %s: %s\n", __FILE__, __LINE__, phase, (err)); \
    abort();                                                            \
  }

#define CheckCondition(cond)                                            \
  if (!(cond)) {                                                        \
    fprintf(stderr, "%s:%d: %s: %s\n", __FILE__, __LINE__, phase, #cond); \
    abort();                                                            \
  }

static void CheckEqual(const char* expected, const char* v, size_t n) {
  if (expected == NULL && v == NULL) {
    // ok
  } else if (expected != NULL && v != NULL && n == strlen(expected) &&
             memcmp(expected, v, n) == 0) {
    // ok
    return;
  } else {
    fprintf(stderr, "%s: expected '%s', got '%s'\n",
            phase,
            (expected ? expected : "(null)"),
            (v ? v : "(null"));
    abort();
  }
}

static void Free(char** ptr) {
  if (*ptr) {
    free(*ptr);
    *ptr = NULL;
  }
}

static void CheckGet(
69 70
    rocksdb_t* db,
    const rocksdb_readoptions_t* options,
71 72 73 74 75
    const char* key,
    const char* expected) {
  char* err = NULL;
  size_t val_len;
  char* val;
76
  val = rocksdb_get(db, options, key, strlen(key), &val_len, &err);
77 78 79 80 81
  CheckNoError(err);
  CheckEqual(expected, val, val_len);
  Free(&val);
}

R
Reed Allman 已提交
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
static void CheckGetCF(
    rocksdb_t* db,
    const rocksdb_readoptions_t* options,
    rocksdb_column_family_handle_t* handle,
    const char* key,
    const char* expected) {
  char* err = NULL;
  size_t val_len;
  char* val;
  val = rocksdb_get_cf(db, options, handle, key, strlen(key), &val_len, &err);
  CheckNoError(err);
  CheckEqual(expected, val, val_len);
  Free(&val);
}


98
static void CheckIter(rocksdb_iterator_t* iter,
99 100 101
                      const char* key, const char* val) {
  size_t len;
  const char* str;
102
  str = rocksdb_iter_key(iter, &len);
103
  CheckEqual(key, str, len);
104
  str = rocksdb_iter_value(iter, &len);
105 106 107
  CheckEqual(val, str, len);
}

108
// Callback from rocksdb_writebatch_iterate()
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
static void CheckPut(void* ptr,
                     const char* k, size_t klen,
                     const char* v, size_t vlen) {
  int* state = (int*) ptr;
  CheckCondition(*state < 2);
  switch (*state) {
    case 0:
      CheckEqual("bar", k, klen);
      CheckEqual("b", v, vlen);
      break;
    case 1:
      CheckEqual("box", k, klen);
      CheckEqual("c", v, vlen);
      break;
  }
  (*state)++;
}

127
// Callback from rocksdb_writebatch_iterate()
128 129 130 131 132 133 134 135 136 137 138
static void CheckDel(void* ptr, const char* k, size_t klen) {
  int* state = (int*) ptr;
  CheckCondition(*state == 2);
  CheckEqual("bar", k, klen);
  (*state)++;
}

static void CmpDestroy(void* arg) { }

static int CmpCompare(void* arg, const char* a, size_t alen,
                      const char* b, size_t blen) {
139
  size_t n = (alen < blen) ? alen : blen;
140 141 142 143 144 145 146 147 148 149 150 151
  int r = memcmp(a, b, n);
  if (r == 0) {
    if (alen < blen) r = -1;
    else if (alen > blen) r = +1;
  }
  return r;
}

static const char* CmpName(void* arg) {
  return "foo";
}

S
Sanjay Ghemawat 已提交
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
// Custom filter policy
static unsigned char fake_filter_result = 1;
static void FilterDestroy(void* arg) { }
static const char* FilterName(void* arg) {
  return "TestFilter";
}
static char* FilterCreate(
    void* arg,
    const char* const* key_array, const size_t* key_length_array,
    int num_keys,
    size_t* filter_length) {
  *filter_length = 4;
  char* result = malloc(4);
  memcpy(result, "fake", 4);
  return result;
}
I
Igor Canadi 已提交
168
static unsigned char FilterKeyMatch(
S
Sanjay Ghemawat 已提交
169 170 171 172 173 174 175 176
    void* arg,
    const char* key, size_t length,
    const char* filter, size_t filter_length) {
  CheckCondition(filter_length == 4);
  CheckCondition(memcmp(filter, "fake", 4) == 0);
  return fake_filter_result;
}

177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
// Custom compaction filter
static void CFilterDestroy(void* arg) {}
static const char* CFilterName(void* arg) { return "foo"; }
static unsigned char CFilterFilter(void* arg, int level, const char* key,
                                   size_t key_length,
                                   const char* existing_value,
                                   size_t value_length, char** new_value,
                                   size_t* new_value_length,
                                   unsigned char* value_changed) {
  if (key_length == 3) {
    if (memcmp(key, "bar", key_length) == 0) {
      return 1;
    } else if (memcmp(key, "baz", key_length) == 0) {
      *value_changed = 1;
      *new_value = "newbazvalue";
      *new_value_length = 11;
      return 0;
    }
  }
  return 0;
}

199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
static void CFilterFactoryDestroy(void* arg) {}
static const char* CFilterFactoryName(void* arg) { return "foo"; }
static rocksdb_compactionfilter_t* CFilterCreate(
    void* arg, rocksdb_compactionfiltercontext_t* context) {
  return rocksdb_compactionfilter_create(NULL, CFilterDestroy, CFilterFilter,
                                         CFilterName);
}

static rocksdb_t* CheckCompaction(rocksdb_t* db, rocksdb_options_t* options,
                                  rocksdb_readoptions_t* roptions,
                                  rocksdb_writeoptions_t* woptions) {
  char* err = NULL;
  db = rocksdb_open(options, dbname, &err);
  CheckNoError(err);
  rocksdb_put(db, woptions, "foo", 3, "foovalue", 8, &err);
  CheckNoError(err);
  CheckGet(db, roptions, "foo", "foovalue");
  rocksdb_put(db, woptions, "bar", 3, "barvalue", 8, &err);
  CheckNoError(err);
  CheckGet(db, roptions, "bar", "barvalue");
  rocksdb_put(db, woptions, "baz", 3, "bazvalue", 8, &err);
  CheckNoError(err);
  CheckGet(db, roptions, "baz", "bazvalue");

  // Force compaction
  rocksdb_compact_range(db, NULL, 0, NULL, 0);
  // should have filtered bar, but not foo
  CheckGet(db, roptions, "foo", "foovalue");
  CheckGet(db, roptions, "bar", NULL);
  CheckGet(db, roptions, "baz", "newbazvalue");
  return db;
}

232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
// Custom compaction filter V2.
static void CompactionFilterV2Destroy(void* arg) { }
static const char* CompactionFilterV2Name(void* arg) {
  return "TestCompactionFilterV2";
}
static void CompactionFilterV2Filter(
    void* arg, int level, size_t num_keys,
    const char* const* keys_list, const size_t* keys_list_sizes,
    const char* const* existing_values_list, const size_t* existing_values_list_sizes,
    char** new_values_list, size_t* new_values_list_sizes,
    unsigned char* to_delete_list) {
  size_t i;
  for (i = 0; i < num_keys; i++) {
    // If any value is "gc", it's removed.
    if (existing_values_list_sizes[i] == 2 && memcmp(existing_values_list[i], "gc", 2) == 0) {
      to_delete_list[i] = 1;
    } else if (existing_values_list_sizes[i] == 6 && memcmp(existing_values_list[i], "gc all", 6) == 0) {
      // If any value is "gc all", all keys are removed.
      size_t j;
      for (j = 0; j < num_keys; j++) {
        to_delete_list[j] = 1;
      }
      return;
    } else if (existing_values_list_sizes[i] == 6 && memcmp(existing_values_list[i], "change", 6) == 0) {
      // If value is "change", set changed value to "changed".
      size_t len;
      len = strlen("changed");
      new_values_list[i] = malloc(len);
      memcpy(new_values_list[i], "changed", len);
      new_values_list_sizes[i] = len;
    } else {
      // Otherwise, no keys are removed.
    }
  }
}

268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
// Custom prefix extractor for compaction filter V2 which extracts first 3 characters.
static void CFV2PrefixExtractorDestroy(void* arg) { }
static char* CFV2PrefixExtractorTransform(void* arg, const char* key, size_t length, size_t* dst_length) {
  // Verify keys are maximum length 4; this verifies fix for a
  // prior bug which was passing the RocksDB-encoded key with
  // logical timestamp suffix instead of parsed user key.
  if (length > 4) {
    fprintf(stderr, "%s:%d: %s: key %s is not user key\n", __FILE__, __LINE__, phase, key);
    abort();
  }
  *dst_length = length < 3 ? length : 3;
  return (char*)key;
}
static unsigned char CFV2PrefixExtractorInDomain(void* state, const char* key, size_t length) {
  return 1;
}
static unsigned char CFV2PrefixExtractorInRange(void* state, const char* key, size_t length) {
  return 1;
}
static const char* CFV2PrefixExtractorName(void* state) {
  return "TestCFV2PrefixExtractor";
}

291
// Custom compaction filter factory V2.
I
Igor Canadi 已提交
292 293 294
static void CompactionFilterFactoryV2Destroy(void* arg) {
  rocksdb_slicetransform_destroy((rocksdb_slicetransform_t*)arg);
}
295 296 297 298
static const char* CompactionFilterFactoryV2Name(void* arg) {
  return "TestCompactionFilterFactoryV2";
}
static rocksdb_compactionfilterv2_t* CompactionFilterFactoryV2Create(
I
Igor Canadi 已提交
299 300 301 302
    void* state, const rocksdb_compactionfiltercontext_t* context) {
  return rocksdb_compactionfilterv2_create(state, CompactionFilterV2Destroy,
                                           CompactionFilterV2Filter,
                                           CompactionFilterV2Name);
303 304
}

305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
// Custom merge operator
static void MergeOperatorDestroy(void* arg) { }
static const char* MergeOperatorName(void* arg) {
  return "TestMergeOperator";
}
static char* MergeOperatorFullMerge(
    void* arg,
    const char* key, size_t key_length,
    const char* existing_value, size_t existing_value_length,
    const char* const* operands_list, const size_t* operands_list_length,
    int num_operands,
    unsigned char* success, size_t* new_value_length) {
  *new_value_length = 4;
  *success = 1;
  char* result = malloc(4);
  memcpy(result, "fake", 4);
  return result;
}
static char* MergeOperatorPartialMerge(
    void* arg,
    const char* key, size_t key_length,
326 327
    const char* const* operands_list, const size_t* operands_list_length,
    int num_operands,
328 329 330 331 332 333 334 335
    unsigned char* success, size_t* new_value_length) {
  *new_value_length = 4;
  *success = 1;
  char* result = malloc(4);
  memcpy(result, "fake", 4);
  return result;
}

336
int main(int argc, char** argv) {
337 338 339 340 341
  rocksdb_t* db;
  rocksdb_comparator_t* cmp;
  rocksdb_cache_t* cache;
  rocksdb_env_t* env;
  rocksdb_options_t* options;
342
  rocksdb_block_based_table_options_t* table_options;
343 344
  rocksdb_readoptions_t* roptions;
  rocksdb_writeoptions_t* woptions;
345
  char* err = NULL;
S
Sanjay Ghemawat 已提交
346
  int run = -1;
347

H
heyongqiang 已提交
348
  snprintf(dbname, sizeof(dbname),
349
           "%s/rocksdb_c_test-%d",
H
heyongqiang 已提交
350
           GetTempDir(),
351 352
           ((int) geteuid()));

353 354 355 356 357
  snprintf(dbbackupname, sizeof(dbbackupname),
           "%s/rocksdb_c_test-%d-backup",
           GetTempDir(),
           ((int) geteuid()));

358
  StartPhase("create_objects");
359 360 361 362 363 364 365 366 367 368 369 370
  cmp = rocksdb_comparator_create(NULL, CmpDestroy, CmpCompare, CmpName);
  env = rocksdb_create_default_env();
  cache = rocksdb_cache_create_lru(100000);

  options = rocksdb_options_create();
  rocksdb_options_set_comparator(options, cmp);
  rocksdb_options_set_error_if_exists(options, 1);
  rocksdb_options_set_env(options, env);
  rocksdb_options_set_info_log(options, NULL);
  rocksdb_options_set_write_buffer_size(options, 100000);
  rocksdb_options_set_paranoid_checks(options, 1);
  rocksdb_options_set_max_open_files(options, 10);
371 372 373 374
  table_options = rocksdb_block_based_options_create();
  rocksdb_block_based_options_set_block_cache(table_options, cache);
  rocksdb_options_set_block_based_table_factory(options, table_options);

375 376 377 378 379 380 381 382 383 384 385 386
  rocksdb_options_set_compression(options, rocksdb_no_compression);
  rocksdb_options_set_compression_options(options, -14, -1, 0);
  int compression_levels[] = {rocksdb_no_compression, rocksdb_no_compression,
                              rocksdb_no_compression, rocksdb_no_compression};
  rocksdb_options_set_compression_per_level(options, compression_levels, 4);

  roptions = rocksdb_readoptions_create();
  rocksdb_readoptions_set_verify_checksums(roptions, 1);
  rocksdb_readoptions_set_fill_cache(roptions, 0);

  woptions = rocksdb_writeoptions_create();
  rocksdb_writeoptions_set_sync(woptions, 1);
387 388

  StartPhase("destroy");
389
  rocksdb_destroy_db(options, dbname, &err);
390 391 392
  Free(&err);

  StartPhase("open_error");
393
  rocksdb_open(options, dbname, &err);
394 395 396 397
  CheckCondition(err != NULL);
  Free(&err);

  StartPhase("open");
398 399
  rocksdb_options_set_create_if_missing(options, 1);
  db = rocksdb_open(options, dbname, &err);
400 401 402 403
  CheckNoError(err);
  CheckGet(db, roptions, "foo", NULL);

  StartPhase("put");
404
  rocksdb_put(db, woptions, "foo", 3, "hello", 5, &err);
405 406 407
  CheckNoError(err);
  CheckGet(db, roptions, "foo", "hello");

408
  StartPhase("backup_and_restore");
409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442
  {
    rocksdb_destroy_db(options, dbbackupname, &err);
    CheckNoError(err);

    rocksdb_backup_engine_t *be = rocksdb_backup_engine_open(options, dbbackupname, &err);
    CheckNoError(err);

    rocksdb_backup_engine_create_new_backup(be, db, &err);
    CheckNoError(err);

    rocksdb_delete(db, woptions, "foo", 3, &err);
    CheckNoError(err);

    rocksdb_close(db);

    rocksdb_destroy_db(options, dbname, &err);
    CheckNoError(err);

    rocksdb_restore_options_t *restore_options = rocksdb_restore_options_create();
    rocksdb_restore_options_set_keep_log_files(restore_options, 0);
    rocksdb_backup_engine_restore_db_from_latest_backup(be, dbname, dbname, restore_options, &err);
    CheckNoError(err);
    rocksdb_restore_options_destroy(restore_options);

    rocksdb_options_set_error_if_exists(options, 0);
    db = rocksdb_open(options, dbname, &err);
    CheckNoError(err);
    rocksdb_options_set_error_if_exists(options, 1);

    CheckGet(db, roptions, "foo", "hello");

    rocksdb_backup_engine_close(be);
  }

S
Sanjay Ghemawat 已提交
443
  StartPhase("compactall");
444
  rocksdb_compact_range(db, NULL, 0, NULL, 0);
S
Sanjay Ghemawat 已提交
445 446 447
  CheckGet(db, roptions, "foo", "hello");

  StartPhase("compactrange");
448
  rocksdb_compact_range(db, "a", 1, "z", 1);
S
Sanjay Ghemawat 已提交
449 450
  CheckGet(db, roptions, "foo", "hello");

451 452
  StartPhase("writebatch");
  {
453 454 455 456 457 458 459
    rocksdb_writebatch_t* wb = rocksdb_writebatch_create();
    rocksdb_writebatch_put(wb, "foo", 3, "a", 1);
    rocksdb_writebatch_clear(wb);
    rocksdb_writebatch_put(wb, "bar", 3, "b", 1);
    rocksdb_writebatch_put(wb, "box", 3, "c", 1);
    rocksdb_writebatch_delete(wb, "bar", 3);
    rocksdb_write(db, woptions, wb, &err);
460 461 462 463 464
    CheckNoError(err);
    CheckGet(db, roptions, "foo", "hello");
    CheckGet(db, roptions, "bar", NULL);
    CheckGet(db, roptions, "box", "c");
    int pos = 0;
465
    rocksdb_writebatch_iterate(wb, &pos, CheckPut, CheckDel);
466
    CheckCondition(pos == 3);
467
    rocksdb_writebatch_destroy(wb);
468 469
  }

470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487
  StartPhase("writebatch_vectors");
  {
    rocksdb_writebatch_t* wb = rocksdb_writebatch_create();
    const char* k_list[2] = { "z", "ap" };
    const size_t k_sizes[2] = { 1, 2 };
    const char* v_list[3] = { "x", "y", "z" };
    const size_t v_sizes[3] = { 1, 1, 1 };
    rocksdb_writebatch_putv(wb, 2, k_list, k_sizes, 3, v_list, v_sizes);
    rocksdb_write(db, woptions, wb, &err);
    CheckNoError(err);
    CheckGet(db, roptions, "zap", "xyz");
    rocksdb_writebatch_delete(wb, "zap", 3);
    rocksdb_write(db, woptions, wb, &err);
    CheckNoError(err);
    CheckGet(db, roptions, "zap", NULL);
    rocksdb_writebatch_destroy(wb);
  }

488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505
  StartPhase("writebatch_rep");
  {
    rocksdb_writebatch_t* wb1 = rocksdb_writebatch_create();
    rocksdb_writebatch_put(wb1, "baz", 3, "d", 1);
    rocksdb_writebatch_put(wb1, "quux", 4, "e", 1);
    rocksdb_writebatch_delete(wb1, "quux", 4);
    size_t repsize1 = 0;
    const char* rep = rocksdb_writebatch_data(wb1, &repsize1);
    rocksdb_writebatch_t* wb2 = rocksdb_writebatch_create_from(rep, repsize1);
    CheckCondition(rocksdb_writebatch_count(wb1) ==
                   rocksdb_writebatch_count(wb2));
    size_t repsize2 = 0;
    CheckCondition(
        memcmp(rep, rocksdb_writebatch_data(wb2, &repsize2), repsize1) == 0);
    rocksdb_writebatch_destroy(wb1);
    rocksdb_writebatch_destroy(wb2);
  }

506 507
  StartPhase("iter");
  {
508 509 510 511
    rocksdb_iterator_t* iter = rocksdb_create_iterator(db, roptions);
    CheckCondition(!rocksdb_iter_valid(iter));
    rocksdb_iter_seek_to_first(iter);
    CheckCondition(rocksdb_iter_valid(iter));
512
    CheckIter(iter, "box", "c");
513
    rocksdb_iter_next(iter);
514
    CheckIter(iter, "foo", "hello");
515
    rocksdb_iter_prev(iter);
516
    CheckIter(iter, "box", "c");
517 518 519
    rocksdb_iter_prev(iter);
    CheckCondition(!rocksdb_iter_valid(iter));
    rocksdb_iter_seek_to_last(iter);
520
    CheckIter(iter, "foo", "hello");
521
    rocksdb_iter_seek(iter, "b", 1);
522
    CheckIter(iter, "box", "c");
523
    rocksdb_iter_get_error(iter, &err);
524
    CheckNoError(err);
525
    rocksdb_iter_destroy(iter);
526 527
  }

R
Reed Allman 已提交
528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554
  StartPhase("multiget");
  {
    const char* keys[3] = { "box", "foo", "notfound" };
    const size_t keys_sizes[3] = { 3, 3, 8 };
    char* vals[3];
    size_t vals_sizes[3];
    char* errs[3];
    rocksdb_multi_get(db, roptions, 3, keys, keys_sizes, vals, vals_sizes, errs);

    int i;
    for (i = 0; i < 3; i++) {
      CheckEqual(NULL, errs[i], 0);
      switch (i) {
      case 0:
        CheckEqual("c", vals[i], vals_sizes[i]);
        break;
      case 1:
        CheckEqual("hello", vals[i], vals_sizes[i]);
        break;
      case 2:
        CheckEqual(NULL, vals[i], vals_sizes[i]);
        break;
      }
      Free(&vals[i]);
    }
  }

555 556 557 558 559 560 561 562 563 564 565
  StartPhase("approximate_sizes");
  {
    int i;
    int n = 20000;
    char keybuf[100];
    char valbuf[100];
    uint64_t sizes[2];
    const char* start[2] = { "a", "k00000000000000010000" };
    size_t start_len[2] = { 1, 21 };
    const char* limit[2] = { "k00000000000000010000", "z" };
    size_t limit_len[2] = { 21, 1 };
566
    rocksdb_writeoptions_set_sync(woptions, 0);
567 568 569
    for (i = 0; i < n; i++) {
      snprintf(keybuf, sizeof(keybuf), "k%020d", i);
      snprintf(valbuf, sizeof(valbuf), "v%020d", i);
570
      rocksdb_put(db, woptions, keybuf, strlen(keybuf), valbuf, strlen(valbuf),
571 572 573
                  &err);
      CheckNoError(err);
    }
574
    rocksdb_approximate_sizes(db, 2, start, start_len, limit, limit_len, sizes);
575 576 577 578 579 580
    CheckCondition(sizes[0] > 0);
    CheckCondition(sizes[1] > 0);
  }

  StartPhase("property");
  {
581
    char* prop = rocksdb_property_value(db, "nosuchprop");
582
    CheckCondition(prop == NULL);
583
    prop = rocksdb_property_value(db, "rocksdb.stats");
584 585 586 587 588 589
    CheckCondition(prop != NULL);
    Free(&prop);
  }

  StartPhase("snapshot");
  {
590 591 592
    const rocksdb_snapshot_t* snap;
    snap = rocksdb_create_snapshot(db);
    rocksdb_delete(db, woptions, "foo", 3, &err);
593
    CheckNoError(err);
594
    rocksdb_readoptions_set_snapshot(roptions, snap);
595
    CheckGet(db, roptions, "foo", "hello");
596
    rocksdb_readoptions_set_snapshot(roptions, NULL);
597
    CheckGet(db, roptions, "foo", NULL);
598
    rocksdb_release_snapshot(db, snap);
599 600 601 602
  }

  StartPhase("repair");
  {
603 604 605 606
    // If we do not compact here, then the lazy deletion of
    // files (https://reviews.facebook.net/D6123) would leave
    // around deleted files and the repair process will find
    // those files and put them back into the database.
607 608 609 610 611
    rocksdb_compact_range(db, NULL, 0, NULL, 0);
    rocksdb_close(db);
    rocksdb_options_set_create_if_missing(options, 0);
    rocksdb_options_set_error_if_exists(options, 0);
    rocksdb_repair_db(options, dbname, &err);
612
    CheckNoError(err);
613
    db = rocksdb_open(options, dbname, &err);
614 615 616 617
    CheckNoError(err);
    CheckGet(db, roptions, "foo", NULL);
    CheckGet(db, roptions, "bar", NULL);
    CheckGet(db, roptions, "box", "c");
618 619
    rocksdb_options_set_create_if_missing(options, 1);
    rocksdb_options_set_error_if_exists(options, 1);
S
Sanjay Ghemawat 已提交
620 621 622 623 624 625
  }

  StartPhase("filter");
  for (run = 0; run < 2; run++) {
    // First run uses custom filter, second run uses bloom filter
    CheckNoError(err);
626
    rocksdb_filterpolicy_t* policy;
S
Sanjay Ghemawat 已提交
627
    if (run == 0) {
628
      policy = rocksdb_filterpolicy_create(
629
          NULL, FilterDestroy, FilterCreate, FilterKeyMatch, NULL, FilterName);
S
Sanjay Ghemawat 已提交
630
    } else {
631
      policy = rocksdb_filterpolicy_create_bloom(10);
S
Sanjay Ghemawat 已提交
632 633
    }

634 635
    rocksdb_block_based_options_set_filter_policy(table_options, policy);

S
Sanjay Ghemawat 已提交
636
    // Create new database
637 638
    rocksdb_close(db);
    rocksdb_destroy_db(options, dbname, &err);
639
    rocksdb_options_set_block_based_table_factory(options, table_options);
640
    db = rocksdb_open(options, dbname, &err);
S
Sanjay Ghemawat 已提交
641
    CheckNoError(err);
642
    rocksdb_put(db, woptions, "foo", 3, "foovalue", 8, &err);
S
Sanjay Ghemawat 已提交
643
    CheckNoError(err);
644
    rocksdb_put(db, woptions, "bar", 3, "barvalue", 8, &err);
S
Sanjay Ghemawat 已提交
645
    CheckNoError(err);
646
    rocksdb_compact_range(db, NULL, 0, NULL, 0);
S
Sanjay Ghemawat 已提交
647 648 649 650 651 652 653 654 655 656 657 658 659 660

    fake_filter_result = 1;
    CheckGet(db, roptions, "foo", "foovalue");
    CheckGet(db, roptions, "bar", "barvalue");
    if (phase == 0) {
      // Must not find value when custom filter returns false
      fake_filter_result = 0;
      CheckGet(db, roptions, "foo", NULL);
      CheckGet(db, roptions, "bar", NULL);
      fake_filter_result = 1;

      CheckGet(db, roptions, "foo", "foovalue");
      CheckGet(db, roptions, "bar", "barvalue");
    }
661 662 663
    // Reset the policy
    rocksdb_block_based_options_set_filter_policy(table_options, NULL);
    rocksdb_options_set_block_based_table_factory(options, table_options);
664 665
  }

666 667
  StartPhase("compaction_filter");
  {
L
Lei Jin 已提交
668 669
    rocksdb_options_t* options_with_filter = rocksdb_options_create();
    rocksdb_options_set_create_if_missing(options_with_filter, 1);
670 671 672 673 674
    rocksdb_compactionfilter_t* cfilter;
    cfilter = rocksdb_compactionfilter_create(NULL, CFilterDestroy,
                                              CFilterFilter, CFilterName);
    // Create new database
    rocksdb_close(db);
L
Lei Jin 已提交
675 676 677
    rocksdb_destroy_db(options_with_filter, dbname, &err);
    rocksdb_options_set_compaction_filter(options_with_filter, cfilter);
    db = CheckCompaction(db, options_with_filter, roptions, woptions);
678

L
Lei Jin 已提交
679
    rocksdb_options_set_compaction_filter(options_with_filter, NULL);
680
    rocksdb_compactionfilter_destroy(cfilter);
L
Lei Jin 已提交
681
    rocksdb_options_destroy(options_with_filter);
682 683 684 685
  }

  StartPhase("compaction_filter_factory");
  {
L
Lei Jin 已提交
686 687
    rocksdb_options_t* options_with_filter_factory = rocksdb_options_create();
    rocksdb_options_set_create_if_missing(options_with_filter_factory, 1);
688 689 690 691 692
    rocksdb_compactionfilterfactory_t* factory;
    factory = rocksdb_compactionfilterfactory_create(
        NULL, CFilterFactoryDestroy, CFilterCreate, CFilterFactoryName);
    // Create new database
    rocksdb_close(db);
L
Lei Jin 已提交
693 694 695 696 697 698 699 700
    rocksdb_destroy_db(options_with_filter_factory, dbname, &err);
    rocksdb_options_set_compaction_filter_factory(options_with_filter_factory,
                                                  factory);
    db = CheckCompaction(db, options_with_filter_factory, roptions, woptions);

    rocksdb_options_set_compaction_filter_factory(
        options_with_filter_factory, NULL);
    rocksdb_options_destroy(options_with_filter_factory);
701 702
  }

703 704 705 706
  StartPhase("compaction_filter_v2");
  {
    rocksdb_compactionfilterfactoryv2_t* factory;
    rocksdb_slicetransform_t* prefix_extractor;
707 708 709 710
    prefix_extractor = rocksdb_slicetransform_create(
        NULL, CFV2PrefixExtractorDestroy, CFV2PrefixExtractorTransform,
        CFV2PrefixExtractorInDomain, CFV2PrefixExtractorInRange,
        CFV2PrefixExtractorName);
711
    factory = rocksdb_compactionfilterfactoryv2_create(
I
Igor Canadi 已提交
712
        prefix_extractor, prefix_extractor, CompactionFilterFactoryV2Destroy,
713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746
        CompactionFilterFactoryV2Create, CompactionFilterFactoryV2Name);
    // Create new database
    rocksdb_close(db);
    rocksdb_destroy_db(options, dbname, &err);
    rocksdb_options_set_compaction_filter_factory_v2(options, factory);
    db = rocksdb_open(options, dbname, &err);
    CheckNoError(err);
    // Only foo2 is GC'd, foo3 is changed.
    rocksdb_put(db, woptions, "foo1", 4, "no gc", 5, &err);
    CheckNoError(err);
    rocksdb_put(db, woptions, "foo2", 4, "gc", 2, &err);
    CheckNoError(err);
    rocksdb_put(db, woptions, "foo3", 4, "change", 6, &err);
    CheckNoError(err);
    // All bars are GC'd.
    rocksdb_put(db, woptions, "bar1", 4, "no gc", 5, &err);
    CheckNoError(err);
    rocksdb_put(db, woptions, "bar2", 4, "gc all", 6, &err);
    CheckNoError(err);
    rocksdb_put(db, woptions, "bar3", 4, "no gc", 5, &err);
    CheckNoError(err);
    // Compact the DB to garbage collect.
    rocksdb_compact_range(db, NULL, 0, NULL, 0);

    // Verify foo entries.
    CheckGet(db, roptions, "foo1", "no gc");
    CheckGet(db, roptions, "foo2", NULL);
    CheckGet(db, roptions, "foo3", "changed");
    // Verify bar entries were all deleted.
    CheckGet(db, roptions, "bar1", NULL);
    CheckGet(db, roptions, "bar2", NULL);
    CheckGet(db, roptions, "bar3", NULL);
  }

747 748 749 750 751 752 753 754 755
  StartPhase("merge_operator");
  {
    rocksdb_mergeoperator_t* merge_operator;
    merge_operator = rocksdb_mergeoperator_create(
        NULL, MergeOperatorDestroy, MergeOperatorFullMerge,
        MergeOperatorPartialMerge, NULL, MergeOperatorName);
    // Create new database
    rocksdb_close(db);
    rocksdb_destroy_db(options, dbname, &err);
I
Igor Canadi 已提交
756
    rocksdb_options_set_merge_operator(options, merge_operator);
757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772
    db = rocksdb_open(options, dbname, &err);
    CheckNoError(err);
    rocksdb_put(db, woptions, "foo", 3, "foovalue", 8, &err);
    CheckNoError(err);
    CheckGet(db, roptions, "foo", "foovalue");
    rocksdb_merge(db, woptions, "foo", 3, "barvalue", 8, &err);
    CheckNoError(err);
    CheckGet(db, roptions, "foo", "fake");

    // Merge of a non-existing value
    rocksdb_merge(db, woptions, "bar", 3, "barvalue", 8, &err);
    CheckNoError(err);
    CheckGet(db, roptions, "bar", "fake");

  }

R
Reed Allman 已提交
773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794
  StartPhase("columnfamilies");
  {
    rocksdb_close(db);
    rocksdb_destroy_db(options, dbname, &err);
    CheckNoError(err)

    rocksdb_options_t* db_options = rocksdb_options_create();
    rocksdb_options_set_create_if_missing(db_options, 1);
    db = rocksdb_open(db_options, dbname, &err);
    CheckNoError(err)
    rocksdb_column_family_handle_t* cfh;
    cfh = rocksdb_create_column_family(db, db_options, "cf1", &err);
    rocksdb_column_family_handle_destroy(cfh);
    CheckNoError(err);
    rocksdb_close(db);

    size_t cflen;
    char** column_fams = rocksdb_list_column_families(db_options, dbname, &cflen, &err);
    CheckNoError(err);
    CheckEqual("default", column_fams[0], 7);
    CheckEqual("cf1", column_fams[1], 3);
    CheckCondition(cflen == 2);
I
Igor Canadi 已提交
795
    rocksdb_list_column_families_destroy(column_fams, cflen);
R
Reed Allman 已提交
796 797

    rocksdb_options_t* cf_options = rocksdb_options_create();
I
Igor Canadi 已提交
798

R
Reed Allman 已提交
799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827
    const char* cf_names[2] = {"default", "cf1"};
    const rocksdb_options_t* cf_opts[2] = {cf_options, cf_options};
    rocksdb_column_family_handle_t* handles[2];
    db = rocksdb_open_column_families(db_options, dbname, 2, cf_names, cf_opts, handles, &err);
    CheckNoError(err);

    rocksdb_put_cf(db, woptions, handles[1], "foo", 3, "hello", 5, &err);
    CheckNoError(err);

    CheckGetCF(db, roptions, handles[1], "foo", "hello");

    rocksdb_delete_cf(db, woptions, handles[1], "foo", 3, &err);
    CheckNoError(err);

    CheckGetCF(db, roptions, handles[1], "foo", NULL);

    rocksdb_writebatch_t* wb = rocksdb_writebatch_create();
    rocksdb_writebatch_put_cf(wb, handles[1], "baz", 3, "a", 1);
    rocksdb_writebatch_clear(wb);
    rocksdb_writebatch_put_cf(wb, handles[1], "bar", 3, "b", 1);
    rocksdb_writebatch_put_cf(wb, handles[1], "box", 3, "c", 1);
    rocksdb_writebatch_delete_cf(wb, handles[1], "bar", 3);
    rocksdb_write(db, woptions, wb, &err);
    CheckNoError(err);
    CheckGetCF(db, roptions, handles[1], "baz", NULL);
    CheckGetCF(db, roptions, handles[1], "bar", NULL);
    CheckGetCF(db, roptions, handles[1], "box", "c");
    rocksdb_writebatch_destroy(wb);

I
Igor Canadi 已提交
828
    const char* keys[3] = { "box", "box", "barfooxx" };
R
Reed Allman 已提交
829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852
    const rocksdb_column_family_handle_t* get_handles[3] = { handles[0], handles[1], handles[1] };
    const size_t keys_sizes[3] = { 3, 3, 8 };
    char* vals[3];
    size_t vals_sizes[3];
    char* errs[3];
    rocksdb_multi_get_cf(db, roptions, get_handles, 3, keys, keys_sizes, vals, vals_sizes, errs);

    int i;
    for (i = 0; i < 3; i++) {
      CheckEqual(NULL, errs[i], 0);
      switch (i) {
      case 0:
        CheckEqual(NULL, vals[i], vals_sizes[i]); // wrong cf
        break;
      case 1:
        CheckEqual("c", vals[i], vals_sizes[i]); // bingo
        break;
      case 2:
        CheckEqual(NULL, vals[i], vals_sizes[i]); // normal not found
        break;
      }
      Free(&vals[i]);
    }

R
Reed Allman 已提交
853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870
    rocksdb_iterator_t* iter = rocksdb_create_iterator_cf(db, roptions, handles[1]);
    CheckCondition(!rocksdb_iter_valid(iter));
    rocksdb_iter_seek_to_first(iter);
    CheckCondition(rocksdb_iter_valid(iter));

    for (i = 0; rocksdb_iter_valid(iter) != 0; rocksdb_iter_next(iter)) {
      i++;
    }
    CheckCondition(i == 1);
    rocksdb_iter_get_error(iter, &err);
    CheckNoError(err);
    rocksdb_iter_destroy(iter);

    rocksdb_drop_column_family(db, handles[1], &err);
    CheckNoError(err);
    for (i = 0; i < 2; i++) {
      rocksdb_column_family_handle_destroy(handles[i]);
    }
I
Igor Canadi 已提交
871 872 873 874
    rocksdb_close(db);
    rocksdb_destroy_db(options, dbname, &err);
    rocksdb_options_destroy(db_options);
    rocksdb_options_destroy(cf_options);
R
Reed Allman 已提交
875 876
  }

877 878 879
  StartPhase("prefix");
  {
    // Create new database
Y
Yueh-Hsuan Chiang 已提交
880
    rocksdb_options_set_allow_mmap_reads(options, 1);
881
    rocksdb_options_set_prefix_extractor(options, rocksdb_slicetransform_create_fixed_prefix(3));
882
    rocksdb_options_set_hash_skip_list_rep(options, 5000, 4, 4);
883
    rocksdb_options_set_plain_table_factory(options, 4, 10, 0.75, 16);
884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916

    db = rocksdb_open(options, dbname, &err);
    CheckNoError(err);

    rocksdb_put(db, woptions, "foo1", 4, "foo", 3, &err);
    CheckNoError(err);
    rocksdb_put(db, woptions, "foo2", 4, "foo", 3, &err);
    CheckNoError(err);
    rocksdb_put(db, woptions, "foo3", 4, "foo", 3, &err);
    CheckNoError(err);
    rocksdb_put(db, woptions, "bar1", 4, "bar", 3, &err);
    CheckNoError(err);
    rocksdb_put(db, woptions, "bar2", 4, "bar", 3, &err);
    CheckNoError(err);
    rocksdb_put(db, woptions, "bar3", 4, "bar", 3, &err);
    CheckNoError(err);

    rocksdb_iterator_t* iter = rocksdb_create_iterator(db, roptions);
    CheckCondition(!rocksdb_iter_valid(iter));

    rocksdb_iter_seek(iter, "bar", 3);
    rocksdb_iter_get_error(iter, &err);
    CheckNoError(err);
    CheckCondition(rocksdb_iter_valid(iter));

    CheckIter(iter, "bar1", "bar");
    rocksdb_iter_next(iter);
    CheckIter(iter, "bar2", "bar");
    rocksdb_iter_next(iter);
    CheckIter(iter, "bar3", "bar");
    rocksdb_iter_get_error(iter, &err);
    CheckNoError(err);
    rocksdb_iter_destroy(iter);
917 918 919

    rocksdb_close(db);
    rocksdb_destroy_db(options, dbname, &err);
920 921
  }

922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937
  StartPhase("cuckoo_options");
  {
    rocksdb_cuckoo_table_options_t* cuckoo_options;
    cuckoo_options = rocksdb_cuckoo_options_create();
    rocksdb_cuckoo_options_set_hash_ratio(cuckoo_options, 0.5);
    rocksdb_cuckoo_options_set_max_search_depth(cuckoo_options, 200);
    rocksdb_cuckoo_options_set_cuckoo_block_size(cuckoo_options, 10);
    rocksdb_cuckoo_options_set_identity_as_first_hash(cuckoo_options, 1);
    rocksdb_cuckoo_options_set_use_module_hash(cuckoo_options, 0);
    rocksdb_options_set_cuckoo_table_factory(options, cuckoo_options);

    db = rocksdb_open(options, dbname, &err);
    CheckNoError(err);

    rocksdb_cuckoo_options_destroy(cuckoo_options);
  }
R
Reed Allman 已提交
938

939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998
  StartPhase("iterate_upper_bound");
  {
    // Create new empty database
    rocksdb_close(db);
    rocksdb_destroy_db(options, dbname, &err);
    CheckNoError(err);

    rocksdb_options_set_prefix_extractor(options, NULL);
    db = rocksdb_open(options, dbname, &err);
    CheckNoError(err);

    rocksdb_put(db, woptions, "a",    1, "0",    1, &err); CheckNoError(err);
    rocksdb_put(db, woptions, "foo",  3, "bar",  3, &err); CheckNoError(err);
    rocksdb_put(db, woptions, "foo1", 4, "bar1", 4, &err); CheckNoError(err);
    rocksdb_put(db, woptions, "g1",   2, "0",    1, &err); CheckNoError(err);

    // testing basic case with no iterate_upper_bound and no prefix_extractor
    {
       rocksdb_readoptions_set_iterate_upper_bound(roptions, NULL, 0);
       rocksdb_iterator_t* iter = rocksdb_create_iterator(db, roptions);

       rocksdb_iter_seek(iter, "foo", 3);
       CheckCondition(rocksdb_iter_valid(iter));
       CheckIter(iter, "foo", "bar");

       rocksdb_iter_next(iter);
       CheckCondition(rocksdb_iter_valid(iter));
       CheckIter(iter, "foo1", "bar1");

       rocksdb_iter_next(iter);
       CheckCondition(rocksdb_iter_valid(iter));
       CheckIter(iter, "g1", "0");

       rocksdb_iter_destroy(iter);
    }

    // testing iterate_upper_bound and forward iterator
    // to make sure it stops at bound
    {
       // iterate_upper_bound points beyond the last expected entry
       rocksdb_readoptions_set_iterate_upper_bound(roptions, "foo2", 4);

       rocksdb_iterator_t* iter = rocksdb_create_iterator(db, roptions);

       rocksdb_iter_seek(iter, "foo", 3);
       CheckCondition(rocksdb_iter_valid(iter));
       CheckIter(iter, "foo", "bar");

       rocksdb_iter_next(iter);
       CheckCondition(rocksdb_iter_valid(iter));
       CheckIter(iter, "foo1", "bar1");

       rocksdb_iter_next(iter);
       // should stop here...
       CheckCondition(!rocksdb_iter_valid(iter));

       rocksdb_iter_destroy(iter);
    }
  }

999
  StartPhase("cleanup");
1000 1001
  rocksdb_close(db);
  rocksdb_options_destroy(options);
1002
  rocksdb_block_based_options_destroy(table_options);
1003 1004 1005 1006 1007
  rocksdb_readoptions_destroy(roptions);
  rocksdb_writeoptions_destroy(woptions);
  rocksdb_cache_destroy(cache);
  rocksdb_comparator_destroy(cmp);
  rocksdb_env_destroy(env);
1008 1009 1010 1011

  fprintf(stderr, "PASS\n");
  return 0;
}
Y
Yueh-Hsuan Chiang 已提交
1012 1013 1014 1015 1016 1017 1018 1019 1020 1021

#else
#include <stdio.h>

int main() {
  fprintf(stderr, "SKIPPED\n");
  return 0;
}

#endif  // !ROCKSDB_LITE