db_bench_sqlite3.cc raw

   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 <sqlite3.h>
   6  #include <stdio.h>
   7  #include <stdlib.h>
   8  
   9  #include "util/histogram.h"
  10  #include "util/random.h"
  11  #include "util/testutil.h"
  12  
  13  // Comma-separated list of operations to run in the specified order
  14  //   Actual benchmarks:
  15  //
  16  //   fillseq       -- write N values in sequential key order in async mode
  17  //   fillseqsync   -- write N/100 values in sequential key order in sync mode
  18  //   fillseqbatch  -- batch write N values in sequential key order in async mode
  19  //   fillrandom    -- write N values in random key order in async mode
  20  //   fillrandsync  -- write N/100 values in random key order in sync mode
  21  //   fillrandbatch -- batch write N values in sequential key order in async mode
  22  //   overwrite     -- overwrite N values in random key order in async mode
  23  //   fillrand100K  -- write N/1000 100K values in random order in async mode
  24  //   fillseq100K   -- write N/1000 100K values in sequential order in async mode
  25  //   readseq       -- read N times sequentially
  26  //   readrandom    -- read N times in random order
  27  //   readrand100K  -- read N/1000 100K values in sequential order in async mode
  28  static const char* FLAGS_benchmarks =
  29      "fillseq,"
  30      "fillseqsync,"
  31      "fillseqbatch,"
  32      "fillrandom,"
  33      "fillrandsync,"
  34      "fillrandbatch,"
  35      "overwrite,"
  36      "overwritebatch,"
  37      "readrandom,"
  38      "readseq,"
  39      "fillrand100K,"
  40      "fillseq100K,"
  41      "readseq,"
  42      "readrand100K,";
  43  
  44  // Number of key/values to place in database
  45  static int FLAGS_num = 1000000;
  46  
  47  // Number of read operations to do.  If negative, do FLAGS_num reads.
  48  static int FLAGS_reads = -1;
  49  
  50  // Size of each value
  51  static int FLAGS_value_size = 100;
  52  
  53  // Print histogram of operation timings
  54  static bool FLAGS_histogram = false;
  55  
  56  // Arrange to generate values that shrink to this fraction of
  57  // their original size after compression
  58  static double FLAGS_compression_ratio = 0.5;
  59  
  60  // Page size. Default 1 KB.
  61  static int FLAGS_page_size = 1024;
  62  
  63  // Number of pages.
  64  // Default cache size = FLAGS_page_size * FLAGS_num_pages = 4 MB.
  65  static int FLAGS_num_pages = 4096;
  66  
  67  // If true, do not destroy the existing database.  If you set this
  68  // flag and also specify a benchmark that wants a fresh database, that
  69  // benchmark will fail.
  70  static bool FLAGS_use_existing_db = false;
  71  
  72  // If true, we allow batch writes to occur
  73  static bool FLAGS_transaction = true;
  74  
  75  // If true, we enable Write-Ahead Logging
  76  static bool FLAGS_WAL_enabled = true;
  77  
  78  // Use the db with the following name.
  79  static const char* FLAGS_db = nullptr;
  80  
  81  inline static void ExecErrorCheck(int status, char* err_msg) {
  82    if (status != SQLITE_OK) {
  83      fprintf(stderr, "SQL error: %s\n", err_msg);
  84      sqlite3_free(err_msg);
  85      exit(1);
  86    }
  87  }
  88  
  89  inline static void StepErrorCheck(int status) {
  90    if (status != SQLITE_DONE) {
  91      fprintf(stderr, "SQL step error: status = %d\n", status);
  92      exit(1);
  93    }
  94  }
  95  
  96  inline static void ErrorCheck(int status) {
  97    if (status != SQLITE_OK) {
  98      fprintf(stderr, "sqlite3 error: status = %d\n", status);
  99      exit(1);
 100    }
 101  }
 102  
 103  inline static void WalCheckpoint(sqlite3* db_) {
 104    // Flush all writes to disk
 105    if (FLAGS_WAL_enabled) {
 106      sqlite3_wal_checkpoint_v2(db_, nullptr, SQLITE_CHECKPOINT_FULL, nullptr,
 107                                nullptr);
 108    }
 109  }
 110  
 111  namespace leveldb {
 112  
 113  // Helper for quickly generating random data.
 114  namespace {
 115  class RandomGenerator {
 116   private:
 117    std::string data_;
 118    int pos_;
 119  
 120   public:
 121    RandomGenerator() {
 122      // We use a limited amount of data over and over again and ensure
 123      // that it is larger than the compression window (32KB), and also
 124      // large enough to serve all typical value sizes we want to write.
 125      Random rnd(301);
 126      std::string piece;
 127      while (data_.size() < 1048576) {
 128        // Add a short fragment that is as compressible as specified
 129        // by FLAGS_compression_ratio.
 130        test::CompressibleString(&rnd, FLAGS_compression_ratio, 100, &piece);
 131        data_.append(piece);
 132      }
 133      pos_ = 0;
 134    }
 135  
 136    Slice Generate(int len) {
 137      if (pos_ + len > data_.size()) {
 138        pos_ = 0;
 139        assert(len < data_.size());
 140      }
 141      pos_ += len;
 142      return Slice(data_.data() + pos_ - len, len);
 143    }
 144  };
 145  
 146  static Slice TrimSpace(Slice s) {
 147    int start = 0;
 148    while (start < s.size() && isspace(s[start])) {
 149      start++;
 150    }
 151    int limit = s.size();
 152    while (limit > start && isspace(s[limit - 1])) {
 153      limit--;
 154    }
 155    return Slice(s.data() + start, limit - start);
 156  }
 157  
 158  }  // namespace
 159  
 160  class Benchmark {
 161   private:
 162    sqlite3* db_;
 163    int db_num_;
 164    int num_;
 165    int reads_;
 166    double start_;
 167    double last_op_finish_;
 168    int64_t bytes_;
 169    std::string message_;
 170    Histogram hist_;
 171    RandomGenerator gen_;
 172    Random rand_;
 173  
 174    // State kept for progress messages
 175    int done_;
 176    int next_report_;  // When to report next
 177  
 178    void PrintHeader() {
 179      const int kKeySize = 16;
 180      PrintEnvironment();
 181      fprintf(stdout, "Keys:       %d bytes each\n", kKeySize);
 182      fprintf(stdout, "Values:     %d bytes each\n", FLAGS_value_size);
 183      fprintf(stdout, "Entries:    %d\n", num_);
 184      fprintf(stdout, "RawSize:    %.1f MB (estimated)\n",
 185              ((static_cast<int64_t>(kKeySize + FLAGS_value_size) * num_) /
 186               1048576.0));
 187      PrintWarnings();
 188      fprintf(stdout, "------------------------------------------------\n");
 189    }
 190  
 191    void PrintWarnings() {
 192  #if defined(__GNUC__) && !defined(__OPTIMIZE__)
 193      fprintf(
 194          stdout,
 195          "WARNING: Optimization is disabled: benchmarks unnecessarily slow\n");
 196  #endif
 197  #ifndef NDEBUG
 198      fprintf(stdout,
 199              "WARNING: Assertions are enabled; benchmarks unnecessarily slow\n");
 200  #endif
 201    }
 202  
 203    void PrintEnvironment() {
 204      fprintf(stderr, "SQLite:     version %s\n", SQLITE_VERSION);
 205  
 206  #if defined(__linux)
 207      time_t now = time(nullptr);
 208      fprintf(stderr, "Date:       %s", ctime(&now));  // ctime() adds newline
 209  
 210      FILE* cpuinfo = fopen("/proc/cpuinfo", "r");
 211      if (cpuinfo != nullptr) {
 212        char line[1000];
 213        int num_cpus = 0;
 214        std::string cpu_type;
 215        std::string cache_size;
 216        while (fgets(line, sizeof(line), cpuinfo) != nullptr) {
 217          const char* sep = strchr(line, ':');
 218          if (sep == nullptr) {
 219            continue;
 220          }
 221          Slice key = TrimSpace(Slice(line, sep - 1 - line));
 222          Slice val = TrimSpace(Slice(sep + 1));
 223          if (key == "model name") {
 224            ++num_cpus;
 225            cpu_type = val.ToString();
 226          } else if (key == "cache size") {
 227            cache_size = val.ToString();
 228          }
 229        }
 230        fclose(cpuinfo);
 231        fprintf(stderr, "CPU:        %d * %s\n", num_cpus, cpu_type.c_str());
 232        fprintf(stderr, "CPUCache:   %s\n", cache_size.c_str());
 233      }
 234  #endif
 235    }
 236  
 237    void Start() {
 238      start_ = Env::Default()->NowMicros() * 1e-6;
 239      bytes_ = 0;
 240      message_.clear();
 241      last_op_finish_ = start_;
 242      hist_.Clear();
 243      done_ = 0;
 244      next_report_ = 100;
 245    }
 246  
 247    void FinishedSingleOp() {
 248      if (FLAGS_histogram) {
 249        double now = Env::Default()->NowMicros() * 1e-6;
 250        double micros = (now - last_op_finish_) * 1e6;
 251        hist_.Add(micros);
 252        if (micros > 20000) {
 253          fprintf(stderr, "long op: %.1f micros%30s\r", micros, "");
 254          fflush(stderr);
 255        }
 256        last_op_finish_ = now;
 257      }
 258  
 259      done_++;
 260      if (done_ >= next_report_) {
 261        if (next_report_ < 1000)
 262          next_report_ += 100;
 263        else if (next_report_ < 5000)
 264          next_report_ += 500;
 265        else if (next_report_ < 10000)
 266          next_report_ += 1000;
 267        else if (next_report_ < 50000)
 268          next_report_ += 5000;
 269        else if (next_report_ < 100000)
 270          next_report_ += 10000;
 271        else if (next_report_ < 500000)
 272          next_report_ += 50000;
 273        else
 274          next_report_ += 100000;
 275        fprintf(stderr, "... finished %d ops%30s\r", done_, "");
 276        fflush(stderr);
 277      }
 278    }
 279  
 280    void Stop(const Slice& name) {
 281      double finish = Env::Default()->NowMicros() * 1e-6;
 282  
 283      // Pretend at least one op was done in case we are running a benchmark
 284      // that does not call FinishedSingleOp().
 285      if (done_ < 1) done_ = 1;
 286  
 287      if (bytes_ > 0) {
 288        char rate[100];
 289        snprintf(rate, sizeof(rate), "%6.1f MB/s",
 290                 (bytes_ / 1048576.0) / (finish - start_));
 291        if (!message_.empty()) {
 292          message_ = std::string(rate) + " " + message_;
 293        } else {
 294          message_ = rate;
 295        }
 296      }
 297  
 298      fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n", name.ToString().c_str(),
 299              (finish - start_) * 1e6 / done_, (message_.empty() ? "" : " "),
 300              message_.c_str());
 301      if (FLAGS_histogram) {
 302        fprintf(stdout, "Microseconds per op:\n%s\n", hist_.ToString().c_str());
 303      }
 304      fflush(stdout);
 305    }
 306  
 307   public:
 308    enum Order { SEQUENTIAL, RANDOM };
 309    enum DBState { FRESH, EXISTING };
 310  
 311    Benchmark()
 312        : db_(nullptr),
 313          db_num_(0),
 314          num_(FLAGS_num),
 315          reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads),
 316          bytes_(0),
 317          rand_(301) {
 318      std::vector<std::string> files;
 319      std::string test_dir;
 320      Env::Default()->GetTestDirectory(&test_dir);
 321      Env::Default()->GetChildren(test_dir, &files);
 322      if (!FLAGS_use_existing_db) {
 323        for (int i = 0; i < files.size(); i++) {
 324          if (Slice(files[i]).starts_with("dbbench_sqlite3")) {
 325            std::string file_name(test_dir);
 326            file_name += "/";
 327            file_name += files[i];
 328            Env::Default()->DeleteFile(file_name.c_str());
 329          }
 330        }
 331      }
 332    }
 333  
 334    ~Benchmark() {
 335      int status = sqlite3_close(db_);
 336      ErrorCheck(status);
 337    }
 338  
 339    void Run() {
 340      PrintHeader();
 341      Open();
 342  
 343      const char* benchmarks = FLAGS_benchmarks;
 344      while (benchmarks != nullptr) {
 345        const char* sep = strchr(benchmarks, ',');
 346        Slice name;
 347        if (sep == nullptr) {
 348          name = benchmarks;
 349          benchmarks = nullptr;
 350        } else {
 351          name = Slice(benchmarks, sep - benchmarks);
 352          benchmarks = sep + 1;
 353        }
 354  
 355        bytes_ = 0;
 356        Start();
 357  
 358        bool known = true;
 359        bool write_sync = false;
 360        if (name == Slice("fillseq")) {
 361          Write(write_sync, SEQUENTIAL, FRESH, num_, FLAGS_value_size, 1);
 362          WalCheckpoint(db_);
 363        } else if (name == Slice("fillseqbatch")) {
 364          Write(write_sync, SEQUENTIAL, FRESH, num_, FLAGS_value_size, 1000);
 365          WalCheckpoint(db_);
 366        } else if (name == Slice("fillrandom")) {
 367          Write(write_sync, RANDOM, FRESH, num_, FLAGS_value_size, 1);
 368          WalCheckpoint(db_);
 369        } else if (name == Slice("fillrandbatch")) {
 370          Write(write_sync, RANDOM, FRESH, num_, FLAGS_value_size, 1000);
 371          WalCheckpoint(db_);
 372        } else if (name == Slice("overwrite")) {
 373          Write(write_sync, RANDOM, EXISTING, num_, FLAGS_value_size, 1);
 374          WalCheckpoint(db_);
 375        } else if (name == Slice("overwritebatch")) {
 376          Write(write_sync, RANDOM, EXISTING, num_, FLAGS_value_size, 1000);
 377          WalCheckpoint(db_);
 378        } else if (name == Slice("fillrandsync")) {
 379          write_sync = true;
 380          Write(write_sync, RANDOM, FRESH, num_ / 100, FLAGS_value_size, 1);
 381          WalCheckpoint(db_);
 382        } else if (name == Slice("fillseqsync")) {
 383          write_sync = true;
 384          Write(write_sync, SEQUENTIAL, FRESH, num_ / 100, FLAGS_value_size, 1);
 385          WalCheckpoint(db_);
 386        } else if (name == Slice("fillrand100K")) {
 387          Write(write_sync, RANDOM, FRESH, num_ / 1000, 100 * 1000, 1);
 388          WalCheckpoint(db_);
 389        } else if (name == Slice("fillseq100K")) {
 390          Write(write_sync, SEQUENTIAL, FRESH, num_ / 1000, 100 * 1000, 1);
 391          WalCheckpoint(db_);
 392        } else if (name == Slice("readseq")) {
 393          ReadSequential();
 394        } else if (name == Slice("readrandom")) {
 395          Read(RANDOM, 1);
 396        } else if (name == Slice("readrand100K")) {
 397          int n = reads_;
 398          reads_ /= 1000;
 399          Read(RANDOM, 1);
 400          reads_ = n;
 401        } else {
 402          known = false;
 403          if (name != Slice()) {  // No error message for empty name
 404            fprintf(stderr, "unknown benchmark '%s'\n", name.ToString().c_str());
 405          }
 406        }
 407        if (known) {
 408          Stop(name);
 409        }
 410      }
 411    }
 412  
 413    void Open() {
 414      assert(db_ == nullptr);
 415  
 416      int status;
 417      char file_name[100];
 418      char* err_msg = nullptr;
 419      db_num_++;
 420  
 421      // Open database
 422      std::string tmp_dir;
 423      Env::Default()->GetTestDirectory(&tmp_dir);
 424      snprintf(file_name, sizeof(file_name), "%s/dbbench_sqlite3-%d.db",
 425               tmp_dir.c_str(), db_num_);
 426      status = sqlite3_open(file_name, &db_);
 427      if (status) {
 428        fprintf(stderr, "open error: %s\n", sqlite3_errmsg(db_));
 429        exit(1);
 430      }
 431  
 432      // Change SQLite cache size
 433      char cache_size[100];
 434      snprintf(cache_size, sizeof(cache_size), "PRAGMA cache_size = %d",
 435               FLAGS_num_pages);
 436      status = sqlite3_exec(db_, cache_size, nullptr, nullptr, &err_msg);
 437      ExecErrorCheck(status, err_msg);
 438  
 439      // FLAGS_page_size is defaulted to 1024
 440      if (FLAGS_page_size != 1024) {
 441        char page_size[100];
 442        snprintf(page_size, sizeof(page_size), "PRAGMA page_size = %d",
 443                 FLAGS_page_size);
 444        status = sqlite3_exec(db_, page_size, nullptr, nullptr, &err_msg);
 445        ExecErrorCheck(status, err_msg);
 446      }
 447  
 448      // Change journal mode to WAL if WAL enabled flag is on
 449      if (FLAGS_WAL_enabled) {
 450        std::string WAL_stmt = "PRAGMA journal_mode = WAL";
 451  
 452        // LevelDB's default cache size is a combined 4 MB
 453        std::string WAL_checkpoint = "PRAGMA wal_autocheckpoint = 4096";
 454        status = sqlite3_exec(db_, WAL_stmt.c_str(), nullptr, nullptr, &err_msg);
 455        ExecErrorCheck(status, err_msg);
 456        status =
 457            sqlite3_exec(db_, WAL_checkpoint.c_str(), nullptr, nullptr, &err_msg);
 458        ExecErrorCheck(status, err_msg);
 459      }
 460  
 461      // Change locking mode to exclusive and create tables/index for database
 462      std::string locking_stmt = "PRAGMA locking_mode = EXCLUSIVE";
 463      std::string create_stmt =
 464          "CREATE TABLE test (key blob, value blob, PRIMARY KEY(key))";
 465      std::string stmt_array[] = {locking_stmt, create_stmt};
 466      int stmt_array_length = sizeof(stmt_array) / sizeof(std::string);
 467      for (int i = 0; i < stmt_array_length; i++) {
 468        status =
 469            sqlite3_exec(db_, stmt_array[i].c_str(), nullptr, nullptr, &err_msg);
 470        ExecErrorCheck(status, err_msg);
 471      }
 472    }
 473  
 474    void Write(bool write_sync, Order order, DBState state, int num_entries,
 475               int value_size, int entries_per_batch) {
 476      // Create new database if state == FRESH
 477      if (state == FRESH) {
 478        if (FLAGS_use_existing_db) {
 479          message_ = "skipping (--use_existing_db is true)";
 480          return;
 481        }
 482        sqlite3_close(db_);
 483        db_ = nullptr;
 484        Open();
 485        Start();
 486      }
 487  
 488      if (num_entries != num_) {
 489        char msg[100];
 490        snprintf(msg, sizeof(msg), "(%d ops)", num_entries);
 491        message_ = msg;
 492      }
 493  
 494      char* err_msg = nullptr;
 495      int status;
 496  
 497      sqlite3_stmt *replace_stmt, *begin_trans_stmt, *end_trans_stmt;
 498      std::string replace_str = "REPLACE INTO test (key, value) VALUES (?, ?)";
 499      std::string begin_trans_str = "BEGIN TRANSACTION;";
 500      std::string end_trans_str = "END TRANSACTION;";
 501  
 502      // Check for synchronous flag in options
 503      std::string sync_stmt =
 504          (write_sync) ? "PRAGMA synchronous = FULL" : "PRAGMA synchronous = OFF";
 505      status = sqlite3_exec(db_, sync_stmt.c_str(), nullptr, nullptr, &err_msg);
 506      ExecErrorCheck(status, err_msg);
 507  
 508      // Preparing sqlite3 statements
 509      status = sqlite3_prepare_v2(db_, replace_str.c_str(), -1, &replace_stmt,
 510                                  nullptr);
 511      ErrorCheck(status);
 512      status = sqlite3_prepare_v2(db_, begin_trans_str.c_str(), -1,
 513                                  &begin_trans_stmt, nullptr);
 514      ErrorCheck(status);
 515      status = sqlite3_prepare_v2(db_, end_trans_str.c_str(), -1, &end_trans_stmt,
 516                                  nullptr);
 517      ErrorCheck(status);
 518  
 519      bool transaction = (entries_per_batch > 1);
 520      for (int i = 0; i < num_entries; i += entries_per_batch) {
 521        // Begin write transaction
 522        if (FLAGS_transaction && transaction) {
 523          status = sqlite3_step(begin_trans_stmt);
 524          StepErrorCheck(status);
 525          status = sqlite3_reset(begin_trans_stmt);
 526          ErrorCheck(status);
 527        }
 528  
 529        // Create and execute SQL statements
 530        for (int j = 0; j < entries_per_batch; j++) {
 531          const char* value = gen_.Generate(value_size).data();
 532  
 533          // Create values for key-value pair
 534          const int k =
 535              (order == SEQUENTIAL) ? i + j : (rand_.Next() % num_entries);
 536          char key[100];
 537          snprintf(key, sizeof(key), "%016d", k);
 538  
 539          // Bind KV values into replace_stmt
 540          status = sqlite3_bind_blob(replace_stmt, 1, key, 16, SQLITE_STATIC);
 541          ErrorCheck(status);
 542          status = sqlite3_bind_blob(replace_stmt, 2, value, value_size,
 543                                     SQLITE_STATIC);
 544          ErrorCheck(status);
 545  
 546          // Execute replace_stmt
 547          bytes_ += value_size + strlen(key);
 548          status = sqlite3_step(replace_stmt);
 549          StepErrorCheck(status);
 550  
 551          // Reset SQLite statement for another use
 552          status = sqlite3_clear_bindings(replace_stmt);
 553          ErrorCheck(status);
 554          status = sqlite3_reset(replace_stmt);
 555          ErrorCheck(status);
 556  
 557          FinishedSingleOp();
 558        }
 559  
 560        // End write transaction
 561        if (FLAGS_transaction && transaction) {
 562          status = sqlite3_step(end_trans_stmt);
 563          StepErrorCheck(status);
 564          status = sqlite3_reset(end_trans_stmt);
 565          ErrorCheck(status);
 566        }
 567      }
 568  
 569      status = sqlite3_finalize(replace_stmt);
 570      ErrorCheck(status);
 571      status = sqlite3_finalize(begin_trans_stmt);
 572      ErrorCheck(status);
 573      status = sqlite3_finalize(end_trans_stmt);
 574      ErrorCheck(status);
 575    }
 576  
 577    void Read(Order order, int entries_per_batch) {
 578      int status;
 579      sqlite3_stmt *read_stmt, *begin_trans_stmt, *end_trans_stmt;
 580  
 581      std::string read_str = "SELECT * FROM test WHERE key = ?";
 582      std::string begin_trans_str = "BEGIN TRANSACTION;";
 583      std::string end_trans_str = "END TRANSACTION;";
 584  
 585      // Preparing sqlite3 statements
 586      status = sqlite3_prepare_v2(db_, begin_trans_str.c_str(), -1,
 587                                  &begin_trans_stmt, nullptr);
 588      ErrorCheck(status);
 589      status = sqlite3_prepare_v2(db_, end_trans_str.c_str(), -1, &end_trans_stmt,
 590                                  nullptr);
 591      ErrorCheck(status);
 592      status = sqlite3_prepare_v2(db_, read_str.c_str(), -1, &read_stmt, nullptr);
 593      ErrorCheck(status);
 594  
 595      bool transaction = (entries_per_batch > 1);
 596      for (int i = 0; i < reads_; i += entries_per_batch) {
 597        // Begin read transaction
 598        if (FLAGS_transaction && transaction) {
 599          status = sqlite3_step(begin_trans_stmt);
 600          StepErrorCheck(status);
 601          status = sqlite3_reset(begin_trans_stmt);
 602          ErrorCheck(status);
 603        }
 604  
 605        // Create and execute SQL statements
 606        for (int j = 0; j < entries_per_batch; j++) {
 607          // Create key value
 608          char key[100];
 609          int k = (order == SEQUENTIAL) ? i + j : (rand_.Next() % reads_);
 610          snprintf(key, sizeof(key), "%016d", k);
 611  
 612          // Bind key value into read_stmt
 613          status = sqlite3_bind_blob(read_stmt, 1, key, 16, SQLITE_STATIC);
 614          ErrorCheck(status);
 615  
 616          // Execute read statement
 617          while ((status = sqlite3_step(read_stmt)) == SQLITE_ROW) {
 618          }
 619          StepErrorCheck(status);
 620  
 621          // Reset SQLite statement for another use
 622          status = sqlite3_clear_bindings(read_stmt);
 623          ErrorCheck(status);
 624          status = sqlite3_reset(read_stmt);
 625          ErrorCheck(status);
 626          FinishedSingleOp();
 627        }
 628  
 629        // End read transaction
 630        if (FLAGS_transaction && transaction) {
 631          status = sqlite3_step(end_trans_stmt);
 632          StepErrorCheck(status);
 633          status = sqlite3_reset(end_trans_stmt);
 634          ErrorCheck(status);
 635        }
 636      }
 637  
 638      status = sqlite3_finalize(read_stmt);
 639      ErrorCheck(status);
 640      status = sqlite3_finalize(begin_trans_stmt);
 641      ErrorCheck(status);
 642      status = sqlite3_finalize(end_trans_stmt);
 643      ErrorCheck(status);
 644    }
 645  
 646    void ReadSequential() {
 647      int status;
 648      sqlite3_stmt* pStmt;
 649      std::string read_str = "SELECT * FROM test ORDER BY key";
 650  
 651      status = sqlite3_prepare_v2(db_, read_str.c_str(), -1, &pStmt, nullptr);
 652      ErrorCheck(status);
 653      for (int i = 0; i < reads_ && SQLITE_ROW == sqlite3_step(pStmt); i++) {
 654        bytes_ += sqlite3_column_bytes(pStmt, 1) + sqlite3_column_bytes(pStmt, 2);
 655        FinishedSingleOp();
 656      }
 657  
 658      status = sqlite3_finalize(pStmt);
 659      ErrorCheck(status);
 660    }
 661  };
 662  
 663  }  // namespace leveldb
 664  
 665  int main(int argc, char** argv) {
 666    std::string default_db_path;
 667    for (int i = 1; i < argc; i++) {
 668      double d;
 669      int n;
 670      char junk;
 671      if (leveldb::Slice(argv[i]).starts_with("--benchmarks=")) {
 672        FLAGS_benchmarks = argv[i] + strlen("--benchmarks=");
 673      } else if (sscanf(argv[i], "--histogram=%d%c", &n, &junk) == 1 &&
 674                 (n == 0 || n == 1)) {
 675        FLAGS_histogram = n;
 676      } else if (sscanf(argv[i], "--compression_ratio=%lf%c", &d, &junk) == 1) {
 677        FLAGS_compression_ratio = d;
 678      } else if (sscanf(argv[i], "--use_existing_db=%d%c", &n, &junk) == 1 &&
 679                 (n == 0 || n == 1)) {
 680        FLAGS_use_existing_db = n;
 681      } else if (sscanf(argv[i], "--num=%d%c", &n, &junk) == 1) {
 682        FLAGS_num = n;
 683      } else if (sscanf(argv[i], "--reads=%d%c", &n, &junk) == 1) {
 684        FLAGS_reads = n;
 685      } else if (sscanf(argv[i], "--value_size=%d%c", &n, &junk) == 1) {
 686        FLAGS_value_size = n;
 687      } else if (leveldb::Slice(argv[i]) == leveldb::Slice("--no_transaction")) {
 688        FLAGS_transaction = false;
 689      } else if (sscanf(argv[i], "--page_size=%d%c", &n, &junk) == 1) {
 690        FLAGS_page_size = n;
 691      } else if (sscanf(argv[i], "--num_pages=%d%c", &n, &junk) == 1) {
 692        FLAGS_num_pages = n;
 693      } else if (sscanf(argv[i], "--WAL_enabled=%d%c", &n, &junk) == 1 &&
 694                 (n == 0 || n == 1)) {
 695        FLAGS_WAL_enabled = n;
 696      } else if (strncmp(argv[i], "--db=", 5) == 0) {
 697        FLAGS_db = argv[i] + 5;
 698      } else {
 699        fprintf(stderr, "Invalid flag '%s'\n", argv[i]);
 700        exit(1);
 701      }
 702    }
 703  
 704    // Choose a location for the test database if none given with --db=<path>
 705    if (FLAGS_db == nullptr) {
 706      leveldb::Env::Default()->GetTestDirectory(&default_db_path);
 707      default_db_path += "/dbbench";
 708      FLAGS_db = default_db_path.c_str();
 709    }
 710  
 711    leveldb::Benchmark benchmark;
 712    benchmark.Run();
 713    return 0;
 714  }
 715