| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839 | // 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 "options/options_parser.h"#include <cmath>#include <map>#include <string>#include <utility>#include <vector>#include "file/read_write_util.h"#include "file/writable_file_writer.h"#include "options/options_helper.h"#include "rocksdb/convenience.h"#include "rocksdb/db.h"#include "test_util/sync_point.h"#include "util/cast_util.h"#include "util/string_util.h"#include "port/port.h"namespace ROCKSDB_NAMESPACE {static const std::string option_file_header =    "# This is a RocksDB option file.\n"    "#\n"    "# For detailed file format spec, please refer to the example file\n"    "# in examples/rocksdb_option_file_example.ini\n"    "#\n"    "\n";Status PersistRocksDBOptions(const DBOptions& db_opt,                             const std::vector<std::string>& cf_names,                             const std::vector<ColumnFamilyOptions>& cf_opts,                             const std::string& file_name, FileSystem* fs) {  TEST_SYNC_POINT("PersistRocksDBOptions:start");  if (cf_names.size() != cf_opts.size()) {    return Status::InvalidArgument(        "cf_names.size() and cf_opts.size() must be the same");  }  std::unique_ptr<FSWritableFile> wf;  Status s =      fs->NewWritableFile(file_name, FileOptions(), &wf, nullptr);  if (!s.ok()) {    return s;  }  std::unique_ptr<WritableFileWriter> writable;  writable.reset(new WritableFileWriter(std::move(wf), file_name, EnvOptions(),                                        nullptr /* statistics */));  std::string options_file_content;  writable->Append(option_file_header + "[" +                   opt_section_titles[kOptionSectionVersion] +                   "]\n"                   "  rocksdb_version=" +                   ToString(ROCKSDB_MAJOR) + "." + ToString(ROCKSDB_MINOR) +                   "." + ToString(ROCKSDB_PATCH) + "\n");  writable->Append("  options_file_version=" +                   ToString(ROCKSDB_OPTION_FILE_MAJOR) + "." +                   ToString(ROCKSDB_OPTION_FILE_MINOR) + "\n");  writable->Append("\n[" + opt_section_titles[kOptionSectionDBOptions] +                   "]\n  ");  s = GetStringFromDBOptions(&options_file_content, db_opt, "\n  ");  if (!s.ok()) {    writable->Close();    return s;  }  writable->Append(options_file_content + "\n");  for (size_t i = 0; i < cf_opts.size(); ++i) {    // CFOptions section    writable->Append("\n[" + opt_section_titles[kOptionSectionCFOptions] +                     " \"" + EscapeOptionString(cf_names[i]) + "\"]\n  ");    s = GetStringFromColumnFamilyOptions(&options_file_content, cf_opts[i],                                         "\n  ");    if (!s.ok()) {      writable->Close();      return s;    }    writable->Append(options_file_content + "\n");    // TableOptions section    auto* tf = cf_opts[i].table_factory.get();    if (tf != nullptr) {      writable->Append("[" + opt_section_titles[kOptionSectionTableOptions] +                       tf->Name() + " \"" + EscapeOptionString(cf_names[i]) +                       "\"]\n  ");      options_file_content.clear();      s = tf->GetOptionString(&options_file_content, "\n  ");      if (!s.ok()) {        return s;      }      writable->Append(options_file_content + "\n");    }  }  writable->Sync(true /* use_fsync */);  writable->Close();  return RocksDBOptionsParser::VerifyRocksDBOptionsFromFile(      db_opt, cf_names, cf_opts, file_name, fs);}RocksDBOptionsParser::RocksDBOptionsParser() { Reset(); }void RocksDBOptionsParser::Reset() {  db_opt_ = DBOptions();  db_opt_map_.clear();  cf_names_.clear();  cf_opts_.clear();  cf_opt_maps_.clear();  has_version_section_ = false;  has_db_options_ = false;  has_default_cf_options_ = false;  for (int i = 0; i < 3; ++i) {    db_version[i] = 0;    opt_file_version[i] = 0;  }}bool RocksDBOptionsParser::IsSection(const std::string& line) {  if (line.size() < 2) {    return false;  }  if (line[0] != '[' || line[line.size() - 1] != ']') {    return false;  }  return true;}Status RocksDBOptionsParser::ParseSection(OptionSection* section,                                          std::string* title,                                          std::string* argument,                                          const std::string& line,                                          const int line_num) {  *section = kOptionSectionUnknown;  // A section is of the form [<SectionName> "<SectionArg>"], where  // "<SectionArg>" is optional.  size_t arg_start_pos = line.find("\"");  size_t arg_end_pos = line.rfind("\"");  // The following if-then check tries to identify whether the input  // section has the optional section argument.  if (arg_start_pos != std::string::npos && arg_start_pos != arg_end_pos) {    *title = TrimAndRemoveComment(line.substr(1, arg_start_pos - 1), true);    *argument = UnescapeOptionString(        line.substr(arg_start_pos + 1, arg_end_pos - arg_start_pos - 1));  } else {    *title = TrimAndRemoveComment(line.substr(1, line.size() - 2), true);    *argument = "";  }  for (int i = 0; i < kOptionSectionUnknown; ++i) {    if (title->find(opt_section_titles[i]) == 0) {      if (i == kOptionSectionVersion || i == kOptionSectionDBOptions ||          i == kOptionSectionCFOptions) {        if (title->size() == opt_section_titles[i].size()) {          // if true, then it indicats equal          *section = static_cast<OptionSection>(i);          return CheckSection(*section, *argument, line_num);        }      } else if (i == kOptionSectionTableOptions) {        // This type of sections has a sufffix at the end of the        // section title        if (title->size() > opt_section_titles[i].size()) {          *section = static_cast<OptionSection>(i);          return CheckSection(*section, *argument, line_num);        }      }    }  }  return Status::InvalidArgument(std::string("Unknown section ") + line);}Status RocksDBOptionsParser::InvalidArgument(const int line_num,                                             const std::string& message) {  return Status::InvalidArgument(      "[RocksDBOptionsParser Error] ",      message + " (at line " + ToString(line_num) + ")");}Status RocksDBOptionsParser::ParseStatement(std::string* name,                                            std::string* value,                                            const std::string& line,                                            const int line_num) {  size_t eq_pos = line.find("=");  if (eq_pos == std::string::npos) {    return InvalidArgument(line_num, "A valid statement must have a '='.");  }  *name = TrimAndRemoveComment(line.substr(0, eq_pos), true);  *value =      TrimAndRemoveComment(line.substr(eq_pos + 1, line.size() - eq_pos - 1));  if (name->empty()) {    return InvalidArgument(line_num,                           "A valid statement must have a variable name.");  }  return Status::OK();}Status RocksDBOptionsParser::Parse(const std::string& file_name, FileSystem* fs,                                   bool ignore_unknown_options,                                   size_t file_readahead_size) {  Reset();  std::unique_ptr<FSSequentialFile> seq_file;  Status s = fs->NewSequentialFile(file_name, FileOptions(), &seq_file,                                   nullptr);  if (!s.ok()) {    return s;  }  SequentialFileReader sf_reader(std::move(seq_file), file_name,                                 file_readahead_size);  OptionSection section = kOptionSectionUnknown;  std::string title;  std::string argument;  std::unordered_map<std::string, std::string> opt_map;  std::istringstream iss;  std::string line;  bool has_data = true;  // we only support single-lined statement.  for (int line_num = 1; ReadOneLine(&iss, &sf_reader, &line, &has_data, &s);       ++line_num) {    if (!s.ok()) {      return s;    }    line = TrimAndRemoveComment(line);    if (line.empty()) {      continue;    }    if (IsSection(line)) {      s = EndSection(section, title, argument, opt_map, ignore_unknown_options);      opt_map.clear();      if (!s.ok()) {        return s;      }      // If the option file is not generated by a higher minor version,      // there shouldn't be any unknown option.      if (ignore_unknown_options && section == kOptionSectionVersion) {        if (db_version[0] < ROCKSDB_MAJOR || (db_version[0] == ROCKSDB_MAJOR &&                                              db_version[1] <= ROCKSDB_MINOR)) {          ignore_unknown_options = false;        }      }      s = ParseSection(§ion, &title, &argument, line, line_num);      if (!s.ok()) {        return s;      }    } else {      std::string name;      std::string value;      s = ParseStatement(&name, &value, line, line_num);      if (!s.ok()) {        return s;      }      opt_map.insert({name, value});    }  }  s = EndSection(section, title, argument, opt_map, ignore_unknown_options);  opt_map.clear();  if (!s.ok()) {    return s;  }  return ValidityCheck();}Status RocksDBOptionsParser::CheckSection(const OptionSection section,                                          const std::string& section_arg,                                          const int line_num) {  if (section == kOptionSectionDBOptions) {    if (has_db_options_) {      return InvalidArgument(          line_num,          "More than one DBOption section found in the option config file");    }    has_db_options_ = true;  } else if (section == kOptionSectionCFOptions) {    bool is_default_cf = (section_arg == kDefaultColumnFamilyName);    if (cf_opts_.size() == 0 && !is_default_cf) {      return InvalidArgument(          line_num,          "Default column family must be the first CFOptions section "          "in the option config file");    } else if (cf_opts_.size() != 0 && is_default_cf) {      return InvalidArgument(          line_num,          "Default column family must be the first CFOptions section "          "in the optio/n config file");    } else if (GetCFOptions(section_arg) != nullptr) {      return InvalidArgument(          line_num,          "Two identical column families found in option config file");    }    has_default_cf_options_ |= is_default_cf;  } else if (section == kOptionSectionTableOptions) {    if (GetCFOptions(section_arg) == nullptr) {      return InvalidArgument(          line_num, std::string(                        "Does not find a matched column family name in "                        "TableOptions section.  Column Family Name:") +                        section_arg);    }  } else if (section == kOptionSectionVersion) {    if (has_version_section_) {      return InvalidArgument(          line_num,          "More than one Version section found in the option config file.");    }    has_version_section_ = true;  }  return Status::OK();}Status RocksDBOptionsParser::ParseVersionNumber(const std::string& ver_name,                                                const std::string& ver_string,                                                const int max_count,                                                int* version) {  int version_index = 0;  int current_number = 0;  int current_digit_count = 0;  bool has_dot = false;  for (int i = 0; i < max_count; ++i) {    version[i] = 0;  }  constexpr int kBufferSize = 200;  char buffer[kBufferSize];  for (size_t i = 0; i < ver_string.size(); ++i) {    if (ver_string[i] == '.') {      if (version_index >= max_count - 1) {        snprintf(buffer, sizeof(buffer) - 1,                 "A valid %s can only contains at most %d dots.",                 ver_name.c_str(), max_count - 1);        return Status::InvalidArgument(buffer);      }      if (current_digit_count == 0) {        snprintf(buffer, sizeof(buffer) - 1,                 "A valid %s must have at least one digit before each dot.",                 ver_name.c_str());        return Status::InvalidArgument(buffer);      }      version[version_index++] = current_number;      current_number = 0;      current_digit_count = 0;      has_dot = true;    } else if (isdigit(ver_string[i])) {      current_number = current_number * 10 + (ver_string[i] - '0');      current_digit_count++;    } else {      snprintf(buffer, sizeof(buffer) - 1,               "A valid %s can only contains dots and numbers.",               ver_name.c_str());      return Status::InvalidArgument(buffer);    }  }  version[version_index] = current_number;  if (has_dot && current_digit_count == 0) {    snprintf(buffer, sizeof(buffer) - 1,             "A valid %s must have at least one digit after each dot.",             ver_name.c_str());    return Status::InvalidArgument(buffer);  }  return Status::OK();}Status RocksDBOptionsParser::EndSection(    const OptionSection section, const std::string& section_title,    const std::string& section_arg,    const std::unordered_map<std::string, std::string>& opt_map,    bool ignore_unknown_options) {  Status s;  if (section == kOptionSectionDBOptions) {    s = GetDBOptionsFromMap(DBOptions(), opt_map, &db_opt_, true,                            ignore_unknown_options);    if (!s.ok()) {      return s;    }    db_opt_map_ = opt_map;  } else if (section == kOptionSectionCFOptions) {    // This condition should be ensured earlier in ParseSection    // so we make an assertion here.    assert(GetCFOptions(section_arg) == nullptr);    cf_names_.emplace_back(section_arg);    cf_opts_.emplace_back();    s = GetColumnFamilyOptionsFromMap(ColumnFamilyOptions(), opt_map,                                      &cf_opts_.back(), true,                                      ignore_unknown_options);    if (!s.ok()) {      return s;    }    // keep the parsed string.    cf_opt_maps_.emplace_back(opt_map);  } else if (section == kOptionSectionTableOptions) {    assert(GetCFOptions(section_arg) != nullptr);    auto* cf_opt = GetCFOptionsImpl(section_arg);    if (cf_opt == nullptr) {      return Status::InvalidArgument(          "The specified column family must be defined before the "          "TableOptions section:",          section_arg);    }    // Ignore error as table factory deserialization is optional    s = GetTableFactoryFromMap(        section_title.substr(            opt_section_titles[kOptionSectionTableOptions].size()),        opt_map, &(cf_opt->table_factory), ignore_unknown_options);    if (!s.ok()) {      return s;    }  } else if (section == kOptionSectionVersion) {    for (const auto pair : opt_map) {      if (pair.first == "rocksdb_version") {        s = ParseVersionNumber(pair.first, pair.second, 3, db_version);        if (!s.ok()) {          return s;        }      } else if (pair.first == "options_file_version") {        s = ParseVersionNumber(pair.first, pair.second, 2, opt_file_version);        if (!s.ok()) {          return s;        }        if (opt_file_version[0] < 1) {          return Status::InvalidArgument(              "A valid options_file_version must be at least 1.");        }      }    }  }  return Status::OK();}Status RocksDBOptionsParser::ValidityCheck() {  if (!has_db_options_) {    return Status::Corruption(        "A RocksDB Option file must have a single DBOptions section");  }  if (!has_default_cf_options_) {    return Status::Corruption(        "A RocksDB Option file must have a single CFOptions:default section");  }  return Status::OK();}std::string RocksDBOptionsParser::TrimAndRemoveComment(const std::string& line,                                                       bool trim_only) {  size_t start = 0;  size_t end = line.size();  // we only support "#" style comment  if (!trim_only) {    size_t search_pos = 0;    while (search_pos < line.size()) {      size_t comment_pos = line.find('#', search_pos);      if (comment_pos == std::string::npos) {        break;      }      if (comment_pos == 0 || line[comment_pos - 1] != '\\') {        end = comment_pos;        break;      }      search_pos = comment_pos + 1;    }  }  while (start < end && isspace(line[start]) != 0) {    ++start;  }  // start < end implies end > 0.  while (start < end && isspace(line[end - 1]) != 0) {    --end;  }  if (start < end) {    return line.substr(start, end - start);  }  return "";}namespace {bool AreEqualDoubles(const double a, const double b) {  return (fabs(a - b) < 0.00001);}}  // namespacebool AreEqualOptions(    const char* opt1, const char* opt2, const OptionTypeInfo& type_info,    const std::string& opt_name,    const std::unordered_map<std::string, std::string>* opt_map) {  const char* offset1 = opt1 + type_info.offset;  const char* offset2 = opt2 + type_info.offset;  switch (type_info.type) {    case OptionType::kBoolean:      return (*reinterpret_cast<const bool*>(offset1) ==              *reinterpret_cast<const bool*>(offset2));    case OptionType::kInt:      return (*reinterpret_cast<const int*>(offset1) ==              *reinterpret_cast<const int*>(offset2));    case OptionType::kInt32T:      return (*reinterpret_cast<const int32_t*>(offset1) ==              *reinterpret_cast<const int32_t*>(offset2));    case OptionType::kInt64T:      {        int64_t v1, v2;        GetUnaligned(reinterpret_cast<const int64_t*>(offset1), &v1);        GetUnaligned(reinterpret_cast<const int64_t*>(offset2), &v2);        return (v1 == v2);      }    case OptionType::kVectorInt:      return (*reinterpret_cast<const std::vector<int>*>(offset1) ==              *reinterpret_cast<const std::vector<int>*>(offset2));    case OptionType::kUInt:      return (*reinterpret_cast<const unsigned int*>(offset1) ==              *reinterpret_cast<const unsigned int*>(offset2));    case OptionType::kUInt32T:      return (*reinterpret_cast<const uint32_t*>(offset1) ==              *reinterpret_cast<const uint32_t*>(offset2));    case OptionType::kUInt64T:      {        uint64_t v1, v2;        GetUnaligned(reinterpret_cast<const uint64_t*>(offset1), &v1);        GetUnaligned(reinterpret_cast<const uint64_t*>(offset2), &v2);        return (v1 == v2);      }    case OptionType::kSizeT:      {        size_t v1, v2;        GetUnaligned(reinterpret_cast<const size_t*>(offset1), &v1);        GetUnaligned(reinterpret_cast<const size_t*>(offset2), &v2);        return (v1 == v2);      }    case OptionType::kString:      return (*reinterpret_cast<const std::string*>(offset1) ==              *reinterpret_cast<const std::string*>(offset2));    case OptionType::kDouble:      return AreEqualDoubles(*reinterpret_cast<const double*>(offset1),                             *reinterpret_cast<const double*>(offset2));    case OptionType::kCompactionStyle:      return (*reinterpret_cast<const CompactionStyle*>(offset1) ==              *reinterpret_cast<const CompactionStyle*>(offset2));    case OptionType::kCompactionPri:      return (*reinterpret_cast<const CompactionPri*>(offset1) ==              *reinterpret_cast<const CompactionPri*>(offset2));    case OptionType::kCompressionType:      return (*reinterpret_cast<const CompressionType*>(offset1) ==              *reinterpret_cast<const CompressionType*>(offset2));    case OptionType::kVectorCompressionType: {      const auto* vec1 =          reinterpret_cast<const std::vector<CompressionType>*>(offset1);      const auto* vec2 =          reinterpret_cast<const std::vector<CompressionType>*>(offset2);      return (*vec1 == *vec2);    }    case OptionType::kChecksumType:      return (*reinterpret_cast<const ChecksumType*>(offset1) ==              *reinterpret_cast<const ChecksumType*>(offset2));    case OptionType::kBlockBasedTableIndexType:      return (          *reinterpret_cast<const BlockBasedTableOptions::IndexType*>(              offset1) ==          *reinterpret_cast<const BlockBasedTableOptions::IndexType*>(offset2));    case OptionType::kBlockBasedTableDataBlockIndexType:      return (          *reinterpret_cast<const BlockBasedTableOptions::DataBlockIndexType*>(              offset1) ==          *reinterpret_cast<const BlockBasedTableOptions::DataBlockIndexType*>(              offset2));    case OptionType::kBlockBasedTableIndexShorteningMode:      return (          *reinterpret_cast<const BlockBasedTableOptions::IndexShorteningMode*>(              offset1) ==          *reinterpret_cast<const BlockBasedTableOptions::IndexShorteningMode*>(              offset2));    case OptionType::kWALRecoveryMode:      return (*reinterpret_cast<const WALRecoveryMode*>(offset1) ==              *reinterpret_cast<const WALRecoveryMode*>(offset2));    case OptionType::kAccessHint:      return (*reinterpret_cast<const DBOptions::AccessHint*>(offset1) ==              *reinterpret_cast<const DBOptions::AccessHint*>(offset2));    case OptionType::kInfoLogLevel:      return (*reinterpret_cast<const InfoLogLevel*>(offset1) ==              *reinterpret_cast<const InfoLogLevel*>(offset2));    case OptionType::kCompactionOptionsFIFO: {      CompactionOptionsFIFO lhs =          *reinterpret_cast<const CompactionOptionsFIFO*>(offset1);      CompactionOptionsFIFO rhs =          *reinterpret_cast<const CompactionOptionsFIFO*>(offset2);      if (lhs.max_table_files_size == rhs.max_table_files_size &&          lhs.allow_compaction == rhs.allow_compaction) {        return true;      }      return false;    }    case OptionType::kCompactionOptionsUniversal: {      CompactionOptionsUniversal lhs =          *reinterpret_cast<const CompactionOptionsUniversal*>(offset1);      CompactionOptionsUniversal rhs =          *reinterpret_cast<const CompactionOptionsUniversal*>(offset2);      if (lhs.size_ratio == rhs.size_ratio &&          lhs.min_merge_width == rhs.min_merge_width &&          lhs.max_merge_width == rhs.max_merge_width &&          lhs.max_size_amplification_percent ==              rhs.max_size_amplification_percent &&          lhs.compression_size_percent == rhs.compression_size_percent &&          lhs.stop_style == rhs.stop_style &&          lhs.allow_trivial_move == rhs.allow_trivial_move) {        return true;      }      return false;    }    default:      if (type_info.verification == OptionVerificationType::kByName ||          type_info.verification ==              OptionVerificationType::kByNameAllowFromNull ||          type_info.verification == OptionVerificationType::kByNameAllowNull) {        std::string value1;        bool result =            SerializeSingleOptionHelper(offset1, type_info.type, &value1);        if (result == false) {          return false;        }        if (opt_map == nullptr) {          return true;        }        auto iter = opt_map->find(opt_name);        if (iter == opt_map->end()) {          return true;        } else {          if (type_info.verification ==              OptionVerificationType::kByNameAllowNull) {            if (iter->second == kNullptrString || value1 == kNullptrString) {              return true;            }          } else if (type_info.verification ==                     OptionVerificationType::kByNameAllowFromNull) {            if (iter->second == kNullptrString) {              return true;            }          }          return (value1 == iter->second);        }      }      return false;  }}Status RocksDBOptionsParser::VerifyRocksDBOptionsFromFile(    const DBOptions& db_opt, const std::vector<std::string>& cf_names,    const std::vector<ColumnFamilyOptions>& cf_opts,    const std::string& file_name, FileSystem* fs,    OptionsSanityCheckLevel sanity_check_level, bool ignore_unknown_options) {  // We infer option file readhead size from log readahead size.  // If it is not given, use 512KB.  size_t file_readahead_size = db_opt.log_readahead_size;  if (file_readahead_size == 0) {    const size_t kDefaultOptionFileReadAheadSize = 512 * 1024;    file_readahead_size = kDefaultOptionFileReadAheadSize;  }  RocksDBOptionsParser parser;  Status s =      parser.Parse(file_name, fs, ignore_unknown_options, file_readahead_size);  if (!s.ok()) {    return s;  }  // Verify DBOptions  s = VerifyDBOptions(db_opt, *parser.db_opt(), parser.db_opt_map(),                      sanity_check_level);  if (!s.ok()) {    return s;  }  // Verify ColumnFamily Name  if (cf_names.size() != parser.cf_names()->size()) {    if (sanity_check_level >= kSanityLevelLooselyCompatible) {      return Status::InvalidArgument(          "[RocksDBOptionParser Error] The persisted options does not have "          "the same number of column family names as the db instance.");    } else if (cf_opts.size() > parser.cf_opts()->size()) {      return Status::InvalidArgument(          "[RocksDBOptionsParser Error]",          "The persisted options file has less number of column family "          "names than that of the specified one.");    }  }  for (size_t i = 0; i < cf_names.size(); ++i) {    if (cf_names[i] != parser.cf_names()->at(i)) {      return Status::InvalidArgument(          "[RocksDBOptionParser Error] The persisted options and the db"          "instance does not have the same name for column family ",          ToString(i));    }  }  // Verify Column Family Options  if (cf_opts.size() != parser.cf_opts()->size()) {    if (sanity_check_level >= kSanityLevelLooselyCompatible) {      return Status::InvalidArgument(          "[RocksDBOptionsParser Error]",          "The persisted options does not have the same number of "          "column families as the db instance.");    } else if (cf_opts.size() > parser.cf_opts()->size()) {      return Status::InvalidArgument(          "[RocksDBOptionsParser Error]",          "The persisted options file has less number of column families "          "than that of the specified number.");    }  }  for (size_t i = 0; i < cf_opts.size(); ++i) {    s = VerifyCFOptions(cf_opts[i], parser.cf_opts()->at(i),                        &(parser.cf_opt_maps()->at(i)), sanity_check_level);    if (!s.ok()) {      return s;    }    s = VerifyTableFactory(cf_opts[i].table_factory.get(),                           parser.cf_opts()->at(i).table_factory.get(),                           sanity_check_level);    if (!s.ok()) {      return s;    }  }  return Status::OK();}Status RocksDBOptionsParser::VerifyDBOptions(    const DBOptions& base_opt, const DBOptions& persisted_opt,    const std::unordered_map<std::string, std::string>* /*opt_map*/,    OptionsSanityCheckLevel sanity_check_level) {  for (auto pair : db_options_type_info) {    if (pair.second.verification == OptionVerificationType::kDeprecated) {      // We skip checking deprecated variables as they might      // contain random values since they might not be initialized      continue;    }    if (DBOptionSanityCheckLevel(pair.first) <= sanity_check_level) {      if (!AreEqualOptions(reinterpret_cast<const char*>(&base_opt),                           reinterpret_cast<const char*>(&persisted_opt),                           pair.second, pair.first, nullptr)) {        constexpr size_t kBufferSize = 2048;        char buffer[kBufferSize];        std::string base_value;        std::string persisted_value;        SerializeSingleOptionHelper(            reinterpret_cast<const char*>(&base_opt) + pair.second.offset,            pair.second.type, &base_value);        SerializeSingleOptionHelper(            reinterpret_cast<const char*>(&persisted_opt) + pair.second.offset,            pair.second.type, &persisted_value);        snprintf(buffer, sizeof(buffer),                 "[RocksDBOptionsParser]: "                 "failed the verification on DBOptions::%s --- "                 "The specified one is %s while the persisted one is %s.\n",                 pair.first.c_str(), base_value.c_str(),                 persisted_value.c_str());        return Status::InvalidArgument(Slice(buffer, strlen(buffer)));      }    }  }  return Status::OK();}Status RocksDBOptionsParser::VerifyCFOptions(    const ColumnFamilyOptions& base_opt,    const ColumnFamilyOptions& persisted_opt,    const std::unordered_map<std::string, std::string>* persisted_opt_map,    OptionsSanityCheckLevel sanity_check_level) {  for (auto& pair : cf_options_type_info) {    if (pair.second.verification == OptionVerificationType::kDeprecated) {      // We skip checking deprecated variables as they might      // contain random values since they might not be initialized      continue;    }    if (CFOptionSanityCheckLevel(pair.first) <= sanity_check_level) {      if (!AreEqualOptions(reinterpret_cast<const char*>(&base_opt),                           reinterpret_cast<const char*>(&persisted_opt),                           pair.second, pair.first, persisted_opt_map)) {        constexpr size_t kBufferSize = 2048;        char buffer[kBufferSize];        std::string base_value;        std::string persisted_value;        SerializeSingleOptionHelper(            reinterpret_cast<const char*>(&base_opt) + pair.second.offset,            pair.second.type, &base_value);        SerializeSingleOptionHelper(            reinterpret_cast<const char*>(&persisted_opt) + pair.second.offset,            pair.second.type, &persisted_value);        snprintf(buffer, sizeof(buffer),                 "[RocksDBOptionsParser]: "                 "failed the verification on ColumnFamilyOptions::%s --- "                 "The specified one is %s while the persisted one is %s.\n",                 pair.first.c_str(), base_value.c_str(),                 persisted_value.c_str());        return Status::InvalidArgument(Slice(buffer, sizeof(buffer)));      }    }  }  return Status::OK();}Status RocksDBOptionsParser::VerifyTableFactory(    const TableFactory* base_tf, const TableFactory* file_tf,    OptionsSanityCheckLevel sanity_check_level) {  if (base_tf && file_tf) {    if (sanity_check_level > kSanityLevelNone &&        std::string(base_tf->Name()) != std::string(file_tf->Name())) {      return Status::Corruption(          "[RocksDBOptionsParser]: "          "failed the verification on TableFactory->Name()");    }    if (base_tf->Name() == BlockBasedTableFactory::kName) {      return VerifyBlockBasedTableFactory(          static_cast_with_check<const BlockBasedTableFactory,                                 const TableFactory>(base_tf),          static_cast_with_check<const BlockBasedTableFactory,                                 const TableFactory>(file_tf),          sanity_check_level);    }    // TODO(yhchiang): add checks for other table factory types  } else {    // TODO(yhchiang): further support sanity check here  }  return Status::OK();}}  // namespace ROCKSDB_NAMESPACE#endif  // !ROCKSDB_LITE
 |