| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363 | //  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.//  This source code is licensed under both the GPLv2 (found in the//  COPYING file in the root directory) and Apache 2.0 License//  (found in the LICENSE.Apache file in the root directory).#ifndef ROCKSDB_LITE#include <cinttypes>#include <cctype>#include <unordered_map>#include "options/options_parser.h"#include "rocksdb/db.h"#include "rocksdb/table.h"#include "rocksdb/utilities/options_util.h"#include "test_util/testharness.h"#include "test_util/testutil.h"#include "util/random.h"#ifndef GFLAGSbool FLAGS_enable_print = false;#else#include "util/gflags_compat.h"using GFLAGS_NAMESPACE::ParseCommandLineFlags;DEFINE_bool(enable_print, false, "Print options generated to console.");#endif  // GFLAGSnamespace ROCKSDB_NAMESPACE {class OptionsUtilTest : public testing::Test { public:  OptionsUtilTest() : rnd_(0xFB) {    env_.reset(new test::StringEnv(Env::Default()));    fs_.reset(new LegacyFileSystemWrapper(env_.get()));    dbname_ = test::PerThreadDBPath("options_util_test");  } protected:  std::unique_ptr<test::StringEnv> env_;  std::unique_ptr<LegacyFileSystemWrapper> fs_;  std::string dbname_;  Random rnd_;};bool IsBlockBasedTableFactory(TableFactory* tf) {  return tf->Name() == BlockBasedTableFactory().Name();}TEST_F(OptionsUtilTest, SaveAndLoad) {  const size_t kCFCount = 5;  DBOptions db_opt;  std::vector<std::string> cf_names;  std::vector<ColumnFamilyOptions> cf_opts;  test::RandomInitDBOptions(&db_opt, &rnd_);  for (size_t i = 0; i < kCFCount; ++i) {    cf_names.push_back(i == 0 ? kDefaultColumnFamilyName                              : test::RandomName(&rnd_, 10));    cf_opts.emplace_back();    test::RandomInitCFOptions(&cf_opts.back(), db_opt, &rnd_);  }  const std::string kFileName = "OPTIONS-123456";  PersistRocksDBOptions(db_opt, cf_names, cf_opts, kFileName, fs_.get());  DBOptions loaded_db_opt;  std::vector<ColumnFamilyDescriptor> loaded_cf_descs;  ASSERT_OK(LoadOptionsFromFile(kFileName, env_.get(), &loaded_db_opt,                                &loaded_cf_descs));  ASSERT_OK(RocksDBOptionsParser::VerifyDBOptions(db_opt, loaded_db_opt));  test::RandomInitDBOptions(&db_opt, &rnd_);  ASSERT_NOK(RocksDBOptionsParser::VerifyDBOptions(db_opt, loaded_db_opt));  for (size_t i = 0; i < kCFCount; ++i) {    ASSERT_EQ(cf_names[i], loaded_cf_descs[i].name);    ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(        cf_opts[i], loaded_cf_descs[i].options));    if (IsBlockBasedTableFactory(cf_opts[i].table_factory.get())) {      ASSERT_OK(RocksDBOptionsParser::VerifyTableFactory(          cf_opts[i].table_factory.get(),          loaded_cf_descs[i].options.table_factory.get()));    }    test::RandomInitCFOptions(&cf_opts[i], db_opt, &rnd_);    ASSERT_NOK(RocksDBOptionsParser::VerifyCFOptions(        cf_opts[i], loaded_cf_descs[i].options));  }  for (size_t i = 0; i < kCFCount; ++i) {    if (cf_opts[i].compaction_filter) {      delete cf_opts[i].compaction_filter;    }  }}TEST_F(OptionsUtilTest, SaveAndLoadWithCacheCheck) {  // creating db  DBOptions db_opt;  db_opt.create_if_missing = true;  // initialize BlockBasedTableOptions  std::shared_ptr<Cache> cache = NewLRUCache(1 * 1024);  BlockBasedTableOptions bbt_opts;  bbt_opts.block_size = 32 * 1024;  // saving cf options  std::vector<ColumnFamilyOptions> cf_opts;  ColumnFamilyOptions default_column_family_opt = ColumnFamilyOptions();  default_column_family_opt.table_factory.reset(      NewBlockBasedTableFactory(bbt_opts));  cf_opts.push_back(default_column_family_opt);  ColumnFamilyOptions cf_opt_sample = ColumnFamilyOptions();  cf_opt_sample.table_factory.reset(NewBlockBasedTableFactory(bbt_opts));  cf_opts.push_back(cf_opt_sample);  ColumnFamilyOptions cf_opt_plain_table_opt = ColumnFamilyOptions();  cf_opt_plain_table_opt.table_factory.reset(NewPlainTableFactory());  cf_opts.push_back(cf_opt_plain_table_opt);  std::vector<std::string> cf_names;  cf_names.push_back(kDefaultColumnFamilyName);  cf_names.push_back("cf_sample");  cf_names.push_back("cf_plain_table_sample");  // Saving DB in file  const std::string kFileName = "OPTIONS-LOAD_CACHE_123456";  PersistRocksDBOptions(db_opt, cf_names, cf_opts, kFileName, fs_.get());  DBOptions loaded_db_opt;  std::vector<ColumnFamilyDescriptor> loaded_cf_descs;  ASSERT_OK(LoadOptionsFromFile(kFileName, env_.get(), &loaded_db_opt,                                &loaded_cf_descs, false, &cache));  for (size_t i = 0; i < loaded_cf_descs.size(); i++) {    if (IsBlockBasedTableFactory(cf_opts[i].table_factory.get())) {      auto* loaded_bbt_opt = reinterpret_cast<BlockBasedTableOptions*>(          loaded_cf_descs[i].options.table_factory->GetOptions());      // Expect the same cache will be loaded      if (loaded_bbt_opt != nullptr) {        ASSERT_EQ(loaded_bbt_opt->block_cache.get(), cache.get());      }    }  }}namespace {class DummyTableFactory : public TableFactory { public:  DummyTableFactory() {}  ~DummyTableFactory() override {}  const char* Name() const override { return "DummyTableFactory"; }  Status NewTableReader(      const TableReaderOptions& /*table_reader_options*/,      std::unique_ptr<RandomAccessFileReader>&& /*file*/,      uint64_t /*file_size*/, std::unique_ptr<TableReader>* /*table_reader*/,      bool /*prefetch_index_and_filter_in_cache*/) const override {    return Status::NotSupported();  }  TableBuilder* NewTableBuilder(      const TableBuilderOptions& /*table_builder_options*/,      uint32_t /*column_family_id*/,      WritableFileWriter* /*file*/) const override {    return nullptr;  }  Status SanitizeOptions(      const DBOptions& /*db_opts*/,      const ColumnFamilyOptions& /*cf_opts*/) const override {    return Status::NotSupported();  }  std::string GetPrintableTableOptions() const override { return ""; }  Status GetOptionString(std::string* /*opt_string*/,                         const std::string& /*delimiter*/) const override {    return Status::OK();  }};class DummyMergeOperator : public MergeOperator { public:  DummyMergeOperator() {}  ~DummyMergeOperator() override {}  bool FullMergeV2(const MergeOperationInput& /*merge_in*/,                   MergeOperationOutput* /*merge_out*/) const override {    return false;  }  bool PartialMergeMulti(const Slice& /*key*/,                         const std::deque<Slice>& /*operand_list*/,                         std::string* /*new_value*/,                         Logger* /*logger*/) const override {    return false;  }  const char* Name() const override { return "DummyMergeOperator"; }};class DummySliceTransform : public SliceTransform { public:  DummySliceTransform() {}  ~DummySliceTransform() override {}  // Return the name of this transformation.  const char* Name() const override { return "DummySliceTransform"; }  // transform a src in domain to a dst in the range  Slice Transform(const Slice& src) const override { return src; }  // determine whether this is a valid src upon the function applies  bool InDomain(const Slice& /*src*/) const override { return false; }  // determine whether dst=Transform(src) for some src  bool InRange(const Slice& /*dst*/) const override { return false; }};}  // namespaceTEST_F(OptionsUtilTest, SanityCheck) {  DBOptions db_opt;  std::vector<ColumnFamilyDescriptor> cf_descs;  const size_t kCFCount = 5;  for (size_t i = 0; i < kCFCount; ++i) {    cf_descs.emplace_back();    cf_descs.back().name =        (i == 0) ? kDefaultColumnFamilyName : test::RandomName(&rnd_, 10);    cf_descs.back().options.table_factory.reset(NewBlockBasedTableFactory());    // Assign non-null values to prefix_extractors except the first cf.    cf_descs.back().options.prefix_extractor.reset(        i != 0 ? test::RandomSliceTransform(&rnd_) : nullptr);    cf_descs.back().options.merge_operator.reset(        test::RandomMergeOperator(&rnd_));  }  db_opt.create_missing_column_families = true;  db_opt.create_if_missing = true;  DestroyDB(dbname_, Options(db_opt, cf_descs[0].options));  DB* db;  std::vector<ColumnFamilyHandle*> handles;  // open and persist the options  ASSERT_OK(DB::Open(db_opt, dbname_, cf_descs, &handles, &db));  // close the db  for (auto* handle : handles) {    delete handle;  }  delete db;  // perform sanity check  ASSERT_OK(      CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs));  ASSERT_GE(kCFCount, 5);  // merge operator  {    std::shared_ptr<MergeOperator> merge_op =        cf_descs[0].options.merge_operator;    ASSERT_NE(merge_op.get(), nullptr);    cf_descs[0].options.merge_operator.reset();    ASSERT_NOK(        CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs));    cf_descs[0].options.merge_operator.reset(new DummyMergeOperator());    ASSERT_NOK(        CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs));    cf_descs[0].options.merge_operator = merge_op;    ASSERT_OK(        CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs));  }  // prefix extractor  {    std::shared_ptr<const SliceTransform> prefix_extractor =        cf_descs[1].options.prefix_extractor;    // It's okay to set prefix_extractor to nullptr.    ASSERT_NE(prefix_extractor, nullptr);    cf_descs[1].options.prefix_extractor.reset();    ASSERT_OK(        CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs));    cf_descs[1].options.prefix_extractor.reset(new DummySliceTransform());    ASSERT_OK(        CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs));    cf_descs[1].options.prefix_extractor = prefix_extractor;    ASSERT_OK(        CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs));  }  // prefix extractor nullptr case  {    std::shared_ptr<const SliceTransform> prefix_extractor =        cf_descs[0].options.prefix_extractor;    // It's okay to set prefix_extractor to nullptr.    ASSERT_EQ(prefix_extractor, nullptr);    cf_descs[0].options.prefix_extractor.reset();    ASSERT_OK(        CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs));    // It's okay to change prefix_extractor from nullptr to non-nullptr    cf_descs[0].options.prefix_extractor.reset(new DummySliceTransform());    ASSERT_OK(        CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs));    cf_descs[0].options.prefix_extractor = prefix_extractor;    ASSERT_OK(        CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs));  }  // comparator  {    test::SimpleSuffixReverseComparator comparator;    auto* prev_comparator = cf_descs[2].options.comparator;    cf_descs[2].options.comparator = &comparator;    ASSERT_NOK(        CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs));    cf_descs[2].options.comparator = prev_comparator;    ASSERT_OK(        CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs));  }  // table factory  {    std::shared_ptr<TableFactory> table_factory =        cf_descs[3].options.table_factory;    ASSERT_NE(table_factory, nullptr);    cf_descs[3].options.table_factory.reset(new DummyTableFactory());    ASSERT_NOK(        CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs));    cf_descs[3].options.table_factory = table_factory;    ASSERT_OK(        CheckOptionsCompatibility(dbname_, Env::Default(), db_opt, cf_descs));  }}}  // namespace ROCKSDB_NAMESPACEint main(int argc, char** argv) {  ::testing::InitGoogleTest(&argc, argv);#ifdef GFLAGS  ParseCommandLineFlags(&argc, &argv, true);#endif  // GFLAGS  return RUN_ALL_TESTS();}#else#include <cstdio>int main(int /*argc*/, char** /*argv*/) {  printf("Skipped in RocksDBLite as utilities are not supported.\n");  return 0;}#endif  // !ROCKSDB_LITE
 |