Option type info functions (#9411)

Summary:
Add methods to set the various functions (Parse, Serialize, Equals) to the OptionTypeInfo.  These methods simplify the number of constructors required for OptionTypeInfo and make the code a little clearer.

Add functions to the OptionTypeInfo for Prepare and Validate.  These methods allow types other than Configurable and Customizable to have Prepare and Validate logic.  These methods could be used by an option to guarantee that its settings were in a range or that a value was initialized.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/9411

Reviewed By: pdillinger

Differential Revision: D36174849

Pulled By: mrambacher

fbshipit-source-id: 72517d8c6bab4723788a4c1a9e16590bff870125
main
mrambacher 3 years ago committed by Facebook GitHub Bot
parent cdaa9576bb
commit bfc6a8ee4a
  1. 1
      HISTORY.md
  2. 1
      db/compaction/compaction_job.cc
  3. 101
      env/composite_env.cc
  4. 60
      env/env.cc
  5. 21
      env/env_test.cc
  6. 9
      include/rocksdb/utilities/customizable_util.h
  7. 292
      include/rocksdb/utilities/options_type.h
  8. 55
      options/cf_options.cc
  9. 36
      options/configurable.cc
  10. 4
      options/customizable.cc
  11. 46
      options/db_options.cc
  12. 103
      options/options_helper.cc
  13. 147
      options/options_test.cc

@ -15,6 +15,7 @@
* EXPERIMENTAL: Add new API AbortIO in file_system to abort the read requests submitted asynchronously. * EXPERIMENTAL: Add new API AbortIO in file_system to abort the read requests submitted asynchronously.
* CompactionFilter::Decision has a new value: kRemoveWithSingleDelete. If CompactionFilter returns this decision, then CompactionIterator will use `SingleDelete` to mark a key as removed. * CompactionFilter::Decision has a new value: kRemoveWithSingleDelete. If CompactionFilter returns this decision, then CompactionIterator will use `SingleDelete` to mark a key as removed.
* Renamed CompactionFilter::Decision::kRemoveWithSingleDelete to kPurge since the latter sounds more general and hides the implementation details of how compaction iterator handles keys. * Renamed CompactionFilter::Decision::kRemoveWithSingleDelete to kPurge since the latter sounds more general and hides the implementation details of how compaction iterator handles keys.
* Added ability to specify functions for Prepare and Validate to OptionsTypeInfo. Added methods to OptionTypeInfo to set the functions via an API. These methods are intended for RocksDB plugin developers for configuration management.
### Bug Fixes ### Bug Fixes
* RocksDB calls FileSystem::Poll API during FilePrefetchBuffer destruction which impacts performance as it waits for read requets completion which is not needed anymore. Calling FileSystem::AbortIO to abort those requests instead fixes that performance issue. * RocksDB calls FileSystem::Poll API during FilePrefetchBuffer destruction which impacts performance as it waits for read requets completion which is not needed anymore. Calling FileSystem::AbortIO to abort those requests instead fixes that performance issue.

@ -2952,6 +2952,7 @@ static std::unordered_map<std::string, OptionTypeInfo> cs_result_type_info = {
const void* addr1, const void* addr2, std::string* mismatch) { const void* addr1, const void* addr2, std::string* mismatch) {
const auto status1 = static_cast<const Status*>(addr1); const auto status1 = static_cast<const Status*>(addr1);
const auto status2 = static_cast<const Status*>(addr2); const auto status2 = static_cast<const Status*>(addr2);
StatusSerializationAdapter adatper1(*status1); StatusSerializationAdapter adatper1(*status1);
StatusSerializationAdapter adapter2(*status2); StatusSerializationAdapter adapter2(*status2);
return OptionTypeInfo::TypesAreEqual(opts, status_adapter_type_info, return OptionTypeInfo::TypesAreEqual(opts, status_adapter_type_info,

101
env/composite_env.cc vendored

@ -5,6 +5,7 @@
// //
#include "env/composite_env_wrapper.h" #include "env/composite_env_wrapper.h"
#include "rocksdb/utilities/options_type.h" #include "rocksdb/utilities/options_type.h"
#include "util/string_util.h"
namespace ROCKSDB_NAMESPACE { namespace ROCKSDB_NAMESPACE {
namespace { namespace {
@ -382,19 +383,49 @@ Status CompositeEnv::NewDirectory(const std::string& name,
} }
namespace { namespace {
static std::unordered_map<std::string, OptionTypeInfo> static std::unordered_map<std::string, OptionTypeInfo> env_wrapper_type_info = {
composite_env_wrapper_type_info = {
#ifndef ROCKSDB_LITE #ifndef ROCKSDB_LITE
{"target", {"target",
{0, OptionType::kCustomizable, OptionVerificationType::kByName, OptionTypeInfo(0, OptionType::kUnknown, OptionVerificationType::kByName,
OptionTypeFlags::kDontSerialize | OptionTypeFlags::kRawPointer, OptionTypeFlags::kDontSerialize)
[](const ConfigOptions& opts, const std::string& /*name*/, .SetParseFunc([](const ConfigOptions& opts,
const std::string& value, void* addr) { const std::string& /*name*/, const std::string& value,
auto target = static_cast<EnvWrapper::Target*>(addr); void* addr) {
return Env::CreateFromString(opts, value, &(target->env), auto target = static_cast<EnvWrapper::Target*>(addr);
&(target->guard)); return Env::CreateFromString(opts, value, &(target->env),
}, &(target->guard));
nullptr, nullptr}}, })
.SetEqualsFunc([](const ConfigOptions& opts,
const std::string& /*name*/, const void* addr1,
const void* addr2, std::string* mismatch) {
const auto target1 = static_cast<const EnvWrapper::Target*>(addr1);
const auto target2 = static_cast<const EnvWrapper::Target*>(addr2);
if (target1->env != nullptr) {
return target1->env->AreEquivalent(opts, target2->env, mismatch);
} else {
return (target2->env == nullptr);
}
})
.SetPrepareFunc([](const ConfigOptions& opts,
const std::string& /*name*/, void* addr) {
auto target = static_cast<EnvWrapper::Target*>(addr);
if (target->guard.get() != nullptr) {
target->env = target->guard.get();
} else if (target->env == nullptr) {
target->env = Env::Default();
}
return target->env->PrepareOptions(opts);
})
.SetValidateFunc([](const DBOptions& db_opts,
const ColumnFamilyOptions& cf_opts,
const std::string& /*name*/, const void* addr) {
const auto target = static_cast<const EnvWrapper::Target*>(addr);
if (target->env == nullptr) {
return Status::InvalidArgument("Target Env not specified");
} else {
return target->env->ValidateOptions(db_opts, cf_opts);
}
})},
#endif // ROCKSDB_LITE #endif // ROCKSDB_LITE
}; };
static std::unordered_map<std::string, OptionTypeInfo> static std::unordered_map<std::string, OptionTypeInfo>
@ -425,7 +456,7 @@ CompositeEnvWrapper::CompositeEnvWrapper(Env* env,
const std::shared_ptr<FileSystem>& fs, const std::shared_ptr<FileSystem>& fs,
const std::shared_ptr<SystemClock>& sc) const std::shared_ptr<SystemClock>& sc)
: CompositeEnv(fs, sc), target_(env) { : CompositeEnv(fs, sc), target_(env) {
RegisterOptions("", &target_, &composite_env_wrapper_type_info); RegisterOptions("", &target_, &env_wrapper_type_info);
RegisterOptions("", &file_system_, &composite_fs_wrapper_type_info); RegisterOptions("", &file_system_, &composite_fs_wrapper_type_info);
RegisterOptions("", &system_clock_, &composite_clock_wrapper_type_info); RegisterOptions("", &system_clock_, &composite_clock_wrapper_type_info);
} }
@ -434,7 +465,7 @@ CompositeEnvWrapper::CompositeEnvWrapper(const std::shared_ptr<Env>& env,
const std::shared_ptr<FileSystem>& fs, const std::shared_ptr<FileSystem>& fs,
const std::shared_ptr<SystemClock>& sc) const std::shared_ptr<SystemClock>& sc)
: CompositeEnv(fs, sc), target_(env) { : CompositeEnv(fs, sc), target_(env) {
RegisterOptions("", &target_, &composite_env_wrapper_type_info); RegisterOptions("", &target_, &env_wrapper_type_info);
RegisterOptions("", &file_system_, &composite_fs_wrapper_type_info); RegisterOptions("", &file_system_, &composite_fs_wrapper_type_info);
RegisterOptions("", &system_clock_, &composite_clock_wrapper_type_info); RegisterOptions("", &system_clock_, &composite_clock_wrapper_type_info);
} }
@ -461,4 +492,46 @@ std::string CompositeEnvWrapper::SerializeOptions(
return options; return options;
} }
#endif // ROCKSDB_LITE #endif // ROCKSDB_LITE
EnvWrapper::EnvWrapper(Env* t) : target_(t) {
RegisterOptions("", &target_, &env_wrapper_type_info);
}
EnvWrapper::EnvWrapper(std::unique_ptr<Env>&& t) : target_(std::move(t)) {
RegisterOptions("", &target_, &env_wrapper_type_info);
}
EnvWrapper::EnvWrapper(const std::shared_ptr<Env>& t) : target_(t) {
RegisterOptions("", &target_, &env_wrapper_type_info);
}
EnvWrapper::~EnvWrapper() {}
Status EnvWrapper::PrepareOptions(const ConfigOptions& options) {
target_.Prepare();
return Env::PrepareOptions(options);
}
#ifndef ROCKSDB_LITE
std::string EnvWrapper::SerializeOptions(const ConfigOptions& config_options,
const std::string& header) const {
auto parent = Env::SerializeOptions(config_options, "");
if (config_options.IsShallow() || target_.env == nullptr ||
target_.env == Env::Default()) {
return parent;
} else {
std::string result = header;
if (!StartsWith(parent, OptionTypeInfo::kIdPropName())) {
result.append(OptionTypeInfo::kIdPropName()).append("=");
}
result.append(parent);
if (!EndsWith(result, config_options.delimiter)) {
result.append(config_options.delimiter);
}
result.append("target=").append(target_.env->ToString(config_options));
return result;
}
}
#endif // ROCKSDB_LITE
} // namespace ROCKSDB_NAMESPACE } // namespace ROCKSDB_NAMESPACE

60
env/env.cc vendored

@ -26,7 +26,6 @@
#include "rocksdb/utilities/object_registry.h" #include "rocksdb/utilities/object_registry.h"
#include "rocksdb/utilities/options_type.h" #include "rocksdb/utilities/options_type.h"
#include "util/autovector.h" #include "util/autovector.h"
#include "util/string_util.h"
namespace ROCKSDB_NAMESPACE { namespace ROCKSDB_NAMESPACE {
namespace { namespace {
@ -1084,65 +1083,6 @@ Status ReadFileToString(Env* env, const std::string& fname, std::string* data) {
return ReadFileToString(fs.get(), fname, data); return ReadFileToString(fs.get(), fname, data);
} }
namespace {
static std::unordered_map<std::string, OptionTypeInfo> env_wrapper_type_info = {
#ifndef ROCKSDB_LITE
{"target",
{0, OptionType::kCustomizable, OptionVerificationType::kByName,
OptionTypeFlags::kDontSerialize | OptionTypeFlags::kRawPointer,
[](const ConfigOptions& opts, const std::string& /*name*/,
const std::string& value, void* addr) {
EnvWrapper::Target* target = static_cast<EnvWrapper::Target*>(addr);
return Env::CreateFromString(opts, value, &(target->env),
&(target->guard));
},
nullptr, nullptr}},
#endif // ROCKSDB_LITE
};
} // namespace
EnvWrapper::EnvWrapper(Env* t) : target_(t) {
RegisterOptions("", &target_, &env_wrapper_type_info);
}
EnvWrapper::EnvWrapper(std::unique_ptr<Env>&& t) : target_(std::move(t)) {
RegisterOptions("", &target_, &env_wrapper_type_info);
}
EnvWrapper::EnvWrapper(const std::shared_ptr<Env>& t) : target_(t) {
RegisterOptions("", &target_, &env_wrapper_type_info);
}
EnvWrapper::~EnvWrapper() {
}
Status EnvWrapper::PrepareOptions(const ConfigOptions& options) {
target_.Prepare();
return Env::PrepareOptions(options);
}
#ifndef ROCKSDB_LITE
std::string EnvWrapper::SerializeOptions(const ConfigOptions& config_options,
const std::string& header) const {
auto parent = Env::SerializeOptions(config_options, "");
if (config_options.IsShallow() || target_.env == nullptr ||
target_.env == Env::Default()) {
return parent;
} else {
std::string result = header;
if (!StartsWith(parent, OptionTypeInfo::kIdPropName())) {
result.append(OptionTypeInfo::kIdPropName()).append("=");
}
result.append(parent);
if (!EndsWith(result, config_options.delimiter)) {
result.append(config_options.delimiter);
}
result.append("target=").append(target_.env->ToString(config_options));
return result;
}
}
#endif // ROCKSDB_LITE
namespace { // anonymous namespace namespace { // anonymous namespace
void AssignEnvOptions(EnvOptions* env_options, const DBOptions& options) { void AssignEnvOptions(EnvOptions* env_options, const DBOptions& options) {

21
env/env_test.cc vendored

@ -44,6 +44,7 @@
#include "env/unique_id_gen.h" #include "env/unique_id_gen.h"
#include "logging/log_buffer.h" #include "logging/log_buffer.h"
#include "logging/logging.h" #include "logging/logging.h"
#include "options/options_helper.h"
#include "port/malloc.h" #include "port/malloc.h"
#include "port/port.h" #include "port/port.h"
#include "port/stack_trace.h" #include "port/stack_trace.h"
@ -2937,7 +2938,7 @@ TEST_F(EnvTest, FailureToCreateLockFile) {
ASSERT_OK(DestroyDir(env, dir)); ASSERT_OK(DestroyDir(env, dir));
} }
TEST_F(EnvTest, CreateDefaultEnv) { TEST_F(CreateEnvTest, CreateDefaultEnv) {
ConfigOptions options; ConfigOptions options;
options.ignore_unsupported_options = false; options.ignore_unsupported_options = false;
@ -2989,7 +2990,7 @@ class WrappedEnv : public EnvWrapper {
} }
}; };
} // namespace } // namespace
TEST_F(EnvTest, CreateMockEnv) { TEST_F(CreateEnvTest, CreateMockEnv) {
ConfigOptions options; ConfigOptions options;
options.ignore_unsupported_options = false; options.ignore_unsupported_options = false;
WrappedEnv::Register(*(options.registry->AddLibrary("test")), ""); WrappedEnv::Register(*(options.registry->AddLibrary("test")), "");
@ -3017,7 +3018,7 @@ TEST_F(EnvTest, CreateMockEnv) {
opt_str = copy->ToString(options); opt_str = copy->ToString(options);
} }
TEST_F(EnvTest, CreateWrappedEnv) { TEST_F(CreateEnvTest, CreateWrappedEnv) {
ConfigOptions options; ConfigOptions options;
options.ignore_unsupported_options = false; options.ignore_unsupported_options = false;
WrappedEnv::Register(*(options.registry->AddLibrary("test")), ""); WrappedEnv::Register(*(options.registry->AddLibrary("test")), "");
@ -3054,7 +3055,7 @@ TEST_F(EnvTest, CreateWrappedEnv) {
ASSERT_TRUE(guard->AreEquivalent(options, copy.get(), &mismatch)); ASSERT_TRUE(guard->AreEquivalent(options, copy.get(), &mismatch));
} }
TEST_F(EnvTest, CreateCompositeEnv) { TEST_F(CreateEnvTest, CreateCompositeEnv) {
ConfigOptions options; ConfigOptions options;
options.ignore_unsupported_options = false; options.ignore_unsupported_options = false;
std::shared_ptr<Env> guard, copy; std::shared_ptr<Env> guard, copy;
@ -3109,6 +3110,18 @@ TEST_F(EnvTest, CreateCompositeEnv) {
ASSERT_NE(env, nullptr); ASSERT_NE(env, nullptr);
ASSERT_NE(env, Env::Default()); ASSERT_NE(env, Env::Default());
ASSERT_TRUE(guard->AreEquivalent(options, copy.get(), &mismatch)); ASSERT_TRUE(guard->AreEquivalent(options, copy.get(), &mismatch));
guard.reset(new CompositeEnvWrapper(nullptr, timed_fs, clock));
ColumnFamilyOptions cf_opts;
DBOptions db_opts;
db_opts.env = guard.get();
auto comp = db_opts.env->CheckedCast<CompositeEnvWrapper>();
ASSERT_NE(comp, nullptr);
ASSERT_EQ(comp->Inner(), nullptr);
ASSERT_NOK(ValidateOptions(db_opts, cf_opts));
ASSERT_OK(db_opts.env->PrepareOptions(options));
ASSERT_NE(comp->Inner(), nullptr);
ASSERT_OK(ValidateOptions(db_opts, cf_opts));
} }
#endif // ROCKSDB_LITE #endif // ROCKSDB_LITE

@ -2,6 +2,15 @@
// This source code is licensed under both the GPLv2 (found in the // This source code is licensed under both the GPLv2 (found in the
// COPYING file in the root directory) and Apache 2.0 License // COPYING file in the root directory) and Apache 2.0 License
// (found in the LICENSE.Apache file in the root directory). // (found in the LICENSE.Apache file in the root directory).
//
// The methods in this file are used to instantiate new Customizable
// instances of objects. These methods are most typically used by
// the "CreateFromString" method of a customizable class.
// If not developing a new Type of customizable class, you probably
// do not need the methods in this file.
//
// See https://github.com/facebook/rocksdb/wiki/RocksDB-Configurable-Objects
// for more information on how to develop and use customizable objects
#pragma once #pragma once
#include <functional> #include <functional>

@ -2,6 +2,15 @@
// This source code is licensed under both the GPLv2 (found in the // This source code is licensed under both the GPLv2 (found in the
// COPYING file in the root directory) and Apache 2.0 License // COPYING file in the root directory) and Apache 2.0 License
// (found in the LICENSE.Apache file in the root directory). // (found in the LICENSE.Apache file in the root directory).
//
// The OptionTypeInfo and related classes provide a framework for
// configuring and validating RocksDB classes via the Options framework.
// This file is part of the public API to allow developers who wish to
// write their own extensions and plugins to take use the Options
// framework in their custom implementations.
//
// See https://github.com/facebook/rocksdb/wiki/RocksDB-Configurable-Objects
// for more information on how to develop and use custom extensions
#pragma once #pragma once
@ -15,6 +24,8 @@
namespace ROCKSDB_NAMESPACE { namespace ROCKSDB_NAMESPACE {
class OptionTypeInfo; class OptionTypeInfo;
struct ColumnFamilyOptions;
struct DBOptions;
// The underlying "class/type" of the option. // The underlying "class/type" of the option.
// This enum is used to determine how the option should // This enum is used to determine how the option should
@ -196,6 +207,16 @@ using EqualsFunc = std::function<bool(
const ConfigOptions& /*opts*/, const std::string& /*name*/, const ConfigOptions& /*opts*/, const std::string& /*name*/,
const void* /*addr1*/, const void* /*addr2*/, std::string* mismatch)>; const void* /*addr1*/, const void* /*addr2*/, std::string* mismatch)>;
// Function for preparing/initializing an option.
using PrepareFunc =
std::function<Status(const ConfigOptions& /*opts*/,
const std::string& /*name*/, void* /*addr*/)>;
// Function for validating an option.
using ValidateFunc = std::function<Status(
const DBOptions& /*db_opts*/, const ColumnFamilyOptions& /*cf_opts*/,
const std::string& /*name*/, const void* /*addr*/)>;
// A struct for storing constant option information such as option name, // A struct for storing constant option information such as option name,
// option type, and offset. // option type, and offset.
class OptionTypeInfo { class OptionTypeInfo {
@ -259,8 +280,9 @@ class OptionTypeInfo {
static OptionTypeInfo Enum( static OptionTypeInfo Enum(
int offset, const std::unordered_map<std::string, T>* const map, int offset, const std::unordered_map<std::string, T>* const map,
OptionTypeFlags flags = OptionTypeFlags::kNone) { OptionTypeFlags flags = OptionTypeFlags::kNone) {
return OptionTypeInfo( OptionTypeInfo info(offset, OptionType::kEnum,
offset, OptionType::kEnum, OptionVerificationType::kNormal, flags, OptionVerificationType::kNormal, flags);
info.SetParseFunc(
// Uses the map argument to convert the input string into // Uses the map argument to convert the input string into
// its corresponding enum value. If value is found in the map, // its corresponding enum value. If value is found in the map,
// addr is updated to the corresponding map entry. // addr is updated to the corresponding map entry.
@ -275,7 +297,8 @@ class OptionTypeInfo {
} else { } else {
return Status::InvalidArgument("No mapping for enum ", name); return Status::InvalidArgument("No mapping for enum ", name);
} }
}, });
info.SetSerializeFunc(
// Uses the map argument to convert the input enum into // Uses the map argument to convert the input enum into
// its corresponding string value. If enum value is found in the map, // its corresponding string value. If enum value is found in the map,
// value is updated to the corresponding string value in the map. // value is updated to the corresponding string value in the map.
@ -291,7 +314,8 @@ class OptionTypeInfo {
} else { } else {
return Status::InvalidArgument("No mapping for enum ", name); return Status::InvalidArgument("No mapping for enum ", name);
} }
}, });
info.SetEqualsFunc(
// Casts addr1 and addr2 to the enum type and returns true if // Casts addr1 and addr2 to the enum type and returns true if
// they are equal, false otherwise. // they are equal, false otherwise.
[](const ConfigOptions&, const std::string&, const void* addr1, [](const ConfigOptions&, const std::string&, const void* addr1,
@ -299,6 +323,7 @@ class OptionTypeInfo {
return (*static_cast<const T*>(addr1) == return (*static_cast<const T*>(addr1) ==
*static_cast<const T*>(addr2)); *static_cast<const T*>(addr2));
}); });
return info;
} // End OptionTypeInfo::Enum } // End OptionTypeInfo::Enum
// Creates an OptionTypeInfo for a Struct type. Structs have a // Creates an OptionTypeInfo for a Struct type. Structs have a
@ -327,21 +352,23 @@ class OptionTypeInfo {
const std::string& struct_name, const std::string& struct_name,
const std::unordered_map<std::string, OptionTypeInfo>* struct_map, const std::unordered_map<std::string, OptionTypeInfo>* struct_map,
int offset, OptionVerificationType verification, OptionTypeFlags flags) { int offset, OptionVerificationType verification, OptionTypeFlags flags) {
return OptionTypeInfo( OptionTypeInfo info(offset, OptionType::kStruct, verification, flags);
offset, OptionType::kStruct, verification, flags, info.SetParseFunc(
// Parses the struct and updates the fields at addr // Parses the struct and updates the fields at addr
[struct_name, struct_map](const ConfigOptions& opts, [struct_name, struct_map](const ConfigOptions& opts,
const std::string& name, const std::string& name,
const std::string& value, void* addr) { const std::string& value, void* addr) {
return ParseStruct(opts, struct_name, struct_map, name, value, addr); return ParseStruct(opts, struct_name, struct_map, name, value, addr);
}, });
info.SetSerializeFunc(
// Serializes the struct options into value // Serializes the struct options into value
[struct_name, struct_map](const ConfigOptions& opts, [struct_name, struct_map](const ConfigOptions& opts,
const std::string& name, const void* addr, const std::string& name, const void* addr,
std::string* value) { std::string* value) {
return SerializeStruct(opts, struct_name, struct_map, name, addr, return SerializeStruct(opts, struct_name, struct_map, name, addr,
value); value);
}, });
info.SetEqualsFunc(
// Compares the struct fields of addr1 and addr2 for equality // Compares the struct fields of addr1 and addr2 for equality
[struct_name, struct_map](const ConfigOptions& opts, [struct_name, struct_map](const ConfigOptions& opts,
const std::string& name, const void* addr1, const std::string& name, const void* addr1,
@ -349,26 +376,16 @@ class OptionTypeInfo {
return StructsAreEqual(opts, struct_name, struct_map, name, addr1, return StructsAreEqual(opts, struct_name, struct_map, name, addr1,
addr2, mismatch); addr2, mismatch);
}); });
return info;
} }
static OptionTypeInfo Struct( static OptionTypeInfo Struct(
const std::string& struct_name, const std::string& struct_name,
const std::unordered_map<std::string, OptionTypeInfo>* struct_map, const std::unordered_map<std::string, OptionTypeInfo>* struct_map,
int offset, OptionVerificationType verification, OptionTypeFlags flags, int offset, OptionVerificationType verification, OptionTypeFlags flags,
const ParseFunc& parse_func) { const ParseFunc& parse_func) {
return OptionTypeInfo( OptionTypeInfo info(
offset, OptionType::kStruct, verification, flags, parse_func, Struct(struct_name, struct_map, offset, verification, flags));
[struct_name, struct_map](const ConfigOptions& opts, return info.SetParseFunc(parse_func);
const std::string& name, const void* addr,
std::string* value) {
return SerializeStruct(opts, struct_name, struct_map, name, addr,
value);
},
[struct_name, struct_map](const ConfigOptions& opts,
const std::string& name, const void* addr1,
const void* addr2, std::string* mismatch) {
return StructsAreEqual(opts, struct_name, struct_map, name, addr1,
addr2, mismatch);
});
} }
template <typename T> template <typename T>
@ -377,30 +394,28 @@ class OptionTypeInfo {
OptionTypeFlags _flags, OptionTypeFlags _flags,
const OptionTypeInfo& elem_info, const OptionTypeInfo& elem_info,
char separator = ':') { char separator = ':') {
return OptionTypeInfo( OptionTypeInfo info(_offset, OptionType::kVector, _verification, _flags);
_offset, OptionType::kVector, _verification, _flags, info.SetParseFunc([elem_info, separator](
[elem_info, separator](const ConfigOptions& opts, const ConfigOptions& opts, const std::string& name,
const std::string& name, const std::string& value, void* addr) {
const std::string& value, void* addr) { auto result = static_cast<std::vector<T>*>(addr);
auto result = static_cast<std::vector<T>*>(addr); return ParseVector<T>(opts, elem_info, separator, name, value, result);
return ParseVector<T>(opts, elem_info, separator, name, value, });
result); info.SetSerializeFunc([elem_info, separator](const ConfigOptions& opts,
}, const std::string& name,
[elem_info, separator](const ConfigOptions& opts, const void* addr,
const std::string& name, const void* addr, std::string* value) {
std::string* value) { const auto& vec = *(static_cast<const std::vector<T>*>(addr));
const auto& vec = *(static_cast<const std::vector<T>*>(addr)); return SerializeVector<T>(opts, elem_info, separator, name, vec, value);
return SerializeVector<T>(opts, elem_info, separator, name, vec, });
value); info.SetEqualsFunc([elem_info](const ConfigOptions& opts,
}, const std::string& name, const void* addr1,
[elem_info](const ConfigOptions& opts, const std::string& name, const void* addr2, std::string* mismatch) {
const void* addr1, const void* addr2, const auto& vec1 = *(static_cast<const std::vector<T>*>(addr1));
std::string* mismatch) { const auto& vec2 = *(static_cast<const std::vector<T>*>(addr2));
const auto& vec1 = *(static_cast<const std::vector<T>*>(addr1)); return VectorsAreEqual<T>(opts, elem_info, name, vec1, vec2, mismatch);
const auto& vec2 = *(static_cast<const std::vector<T>*>(addr2)); });
return VectorsAreEqual<T>(opts, elem_info, name, vec1, vec2, return info;
mismatch);
});
} }
// Create a new std::shared_ptr<Customizable> OptionTypeInfo // Create a new std::shared_ptr<Customizable> OptionTypeInfo
@ -416,7 +431,19 @@ class OptionTypeInfo {
static OptionTypeInfo AsCustomSharedPtr(int offset, static OptionTypeInfo AsCustomSharedPtr(int offset,
OptionVerificationType ovt, OptionVerificationType ovt,
OptionTypeFlags flags) { OptionTypeFlags flags) {
return AsCustomSharedPtr<T>(offset, ovt, flags, nullptr, nullptr); OptionTypeInfo info(offset, OptionType::kCustomizable, ovt,
flags | OptionTypeFlags::kShared);
return info.SetParseFunc([](const ConfigOptions& opts,
const std::string& name,
const std::string& value, void* addr) {
auto* shared = static_cast<std::shared_ptr<T>*>(addr);
if (name == kIdPropName() && value.empty()) {
shared->reset();
return Status::OK();
} else {
return T::CreateFromString(opts, value, shared);
}
});
} }
template <typename T> template <typename T>
@ -425,20 +452,10 @@ class OptionTypeInfo {
OptionTypeFlags flags, OptionTypeFlags flags,
const SerializeFunc& serialize_func, const SerializeFunc& serialize_func,
const EqualsFunc& equals_func) { const EqualsFunc& equals_func) {
return OptionTypeInfo( OptionTypeInfo info(AsCustomSharedPtr<T>(offset, ovt, flags));
offset, OptionType::kCustomizable, ovt, info.SetSerializeFunc(serialize_func);
flags | OptionTypeFlags::kShared, info.SetEqualsFunc(equals_func);
[](const ConfigOptions& opts, const std::string& name, return info;
const std::string& value, void* addr) {
auto* shared = static_cast<std::shared_ptr<T>*>(addr);
if (name == kIdPropName() && value.empty()) {
shared->reset();
return Status::OK();
} else {
return T::CreateFromString(opts, value, shared);
}
},
serialize_func, equals_func);
} }
// Create a new std::unique_ptr<Customizable> OptionTypeInfo // Create a new std::unique_ptr<Customizable> OptionTypeInfo
@ -454,7 +471,19 @@ class OptionTypeInfo {
static OptionTypeInfo AsCustomUniquePtr(int offset, static OptionTypeInfo AsCustomUniquePtr(int offset,
OptionVerificationType ovt, OptionVerificationType ovt,
OptionTypeFlags flags) { OptionTypeFlags flags) {
return AsCustomUniquePtr<T>(offset, ovt, flags, nullptr, nullptr); OptionTypeInfo info(offset, OptionType::kCustomizable, ovt,
flags | OptionTypeFlags::kUnique);
return info.SetParseFunc([](const ConfigOptions& opts,
const std::string& name,
const std::string& value, void* addr) {
auto* unique = static_cast<std::unique_ptr<T>*>(addr);
if (name == kIdPropName() && value.empty()) {
unique->reset();
return Status::OK();
} else {
return T::CreateFromString(opts, value, unique);
}
});
} }
template <typename T> template <typename T>
@ -463,20 +492,10 @@ class OptionTypeInfo {
OptionTypeFlags flags, OptionTypeFlags flags,
const SerializeFunc& serialize_func, const SerializeFunc& serialize_func,
const EqualsFunc& equals_func) { const EqualsFunc& equals_func) {
return OptionTypeInfo( OptionTypeInfo info(AsCustomUniquePtr<T>(offset, ovt, flags));
offset, OptionType::kCustomizable, ovt, info.SetSerializeFunc(serialize_func);
flags | OptionTypeFlags::kUnique, info.SetEqualsFunc(equals_func);
[](const ConfigOptions& opts, const std::string& name, return info;
const std::string& value, void* addr) {
auto* unique = static_cast<std::unique_ptr<T>*>(addr);
if (name == kIdPropName() && value.empty()) {
unique->reset();
return Status::OK();
} else {
return T::CreateFromString(opts, value, unique);
}
},
serialize_func, equals_func);
} }
// Create a new Customizable* OptionTypeInfo // Create a new Customizable* OptionTypeInfo
@ -491,7 +510,19 @@ class OptionTypeInfo {
template <typename T> template <typename T>
static OptionTypeInfo AsCustomRawPtr(int offset, OptionVerificationType ovt, static OptionTypeInfo AsCustomRawPtr(int offset, OptionVerificationType ovt,
OptionTypeFlags flags) { OptionTypeFlags flags) {
return AsCustomRawPtr<T>(offset, ovt, flags, nullptr, nullptr); OptionTypeInfo info(offset, OptionType::kCustomizable, ovt,
flags | OptionTypeFlags::kRawPointer);
return info.SetParseFunc([](const ConfigOptions& opts,
const std::string& name,
const std::string& value, void* addr) {
auto** pointer = static_cast<T**>(addr);
if (name == kIdPropName() && value.empty()) {
*pointer = nullptr;
return Status::OK();
} else {
return T::CreateFromString(opts, value, pointer);
}
});
} }
template <typename T> template <typename T>
@ -499,20 +530,34 @@ class OptionTypeInfo {
OptionTypeFlags flags, OptionTypeFlags flags,
const SerializeFunc& serialize_func, const SerializeFunc& serialize_func,
const EqualsFunc& equals_func) { const EqualsFunc& equals_func) {
return OptionTypeInfo( OptionTypeInfo info(AsCustomRawPtr<T>(offset, ovt, flags));
offset, OptionType::kCustomizable, ovt, info.SetSerializeFunc(serialize_func);
flags | OptionTypeFlags::kRawPointer, info.SetEqualsFunc(equals_func);
[](const ConfigOptions& opts, const std::string& name, return info;
const std::string& value, void* addr) { }
auto** pointer = static_cast<T**>(addr);
if (name == kIdPropName() && value.empty()) { OptionTypeInfo& SetParseFunc(const ParseFunc& f) {
*pointer = nullptr; parse_func_ = f;
return Status::OK(); return *this;
} else { }
return T::CreateFromString(opts, value, pointer);
} OptionTypeInfo& SetSerializeFunc(const SerializeFunc& f) {
}, serialize_func_ = f;
serialize_func, equals_func); return *this;
}
OptionTypeInfo& SetEqualsFunc(const EqualsFunc& f) {
equals_func_ = f;
return *this;
}
OptionTypeInfo& SetPrepareFunc(const PrepareFunc& f) {
prepare_func_ = f;
return *this;
}
OptionTypeInfo& SetValidateFunc(const ValidateFunc& f) {
validate_func_ = f;
return *this;
} }
bool IsEnabled(OptionTypeFlags otf) const { return (flags_ & otf) == otf; } bool IsEnabled(OptionTypeFlags otf) const { return (flags_ & otf) == otf; }
@ -569,6 +614,24 @@ class OptionTypeInfo {
} }
} }
bool ShouldPrepare() const {
if (IsDeprecated() || IsAlias()) {
return false;
} else if (IsEnabled(OptionTypeFlags::kDontPrepare)) {
return false;
} else {
return (prepare_func_ != nullptr || IsConfigurable());
}
}
bool ShouldValidate() const {
if (IsDeprecated() || IsAlias()) {
return false;
} else {
return (validate_func_ != nullptr || IsConfigurable());
}
}
// Returns true if the option is allowed to be null. // Returns true if the option is allowed to be null.
// Options can be null if the verification type is allow from null // Options can be null if the verification type is allow from null
// or if the flags specify allow null. // or if the flags specify allow null.
@ -599,6 +662,26 @@ class OptionTypeInfo {
bool IsCustomizable() const { return (type_ == OptionType::kCustomizable); } bool IsCustomizable() const { return (type_ == OptionType::kCustomizable); }
inline const void* GetOffset(const void* base) const {
return static_cast<const char*>(base) + offset_;
}
inline void* GetOffset(void* base) const {
return static_cast<char*>(base) + offset_;
}
template <typename T>
const T* GetOffsetAs(const void* base) const {
const void* addr = GetOffset(base);
return static_cast<const T*>(addr);
}
template <typename T>
T* GetOffsetAs(void* base) const {
void* addr = GetOffset(base);
return static_cast<T*>(addr);
}
// Returns the underlying pointer for the type at base_addr // Returns the underlying pointer for the type at base_addr
// The value returned is the underlying "raw" pointer, offset from base. // The value returned is the underlying "raw" pointer, offset from base.
template <typename T> template <typename T>
@ -606,20 +689,17 @@ class OptionTypeInfo {
if (base_addr == nullptr) { if (base_addr == nullptr) {
return nullptr; return nullptr;
} }
const void* opt_addr = static_cast<const char*>(base_addr) + offset_;
if (IsUniquePtr()) { if (IsUniquePtr()) {
const std::unique_ptr<T>* ptr = const auto ptr = GetOffsetAs<std::unique_ptr<T>>(base_addr);
static_cast<const std::unique_ptr<T>*>(opt_addr);
return ptr->get(); return ptr->get();
} else if (IsSharedPtr()) { } else if (IsSharedPtr()) {
const std::shared_ptr<T>* ptr = const auto ptr = GetOffsetAs<std::shared_ptr<T>>(base_addr);
static_cast<const std::shared_ptr<T>*>(opt_addr);
return ptr->get(); return ptr->get();
} else if (IsRawPtr()) { } else if (IsRawPtr()) {
const T* const* ptr = static_cast<const T* const*>(opt_addr); const T* const* ptr = GetOffsetAs<T* const>(base_addr);
return *ptr; return *ptr;
} else { } else {
return static_cast<const T*>(opt_addr); return GetOffsetAs<T>(base_addr);
} }
} }
@ -630,18 +710,17 @@ class OptionTypeInfo {
if (base_addr == nullptr) { if (base_addr == nullptr) {
return nullptr; return nullptr;
} }
void* opt_addr = static_cast<char*>(base_addr) + offset_;
if (IsUniquePtr()) { if (IsUniquePtr()) {
std::unique_ptr<T>* ptr = static_cast<std::unique_ptr<T>*>(opt_addr); auto ptr = GetOffsetAs<std::unique_ptr<T>>(base_addr);
return ptr->get(); return ptr->get();
} else if (IsSharedPtr()) { } else if (IsSharedPtr()) {
std::shared_ptr<T>* ptr = static_cast<std::shared_ptr<T>*>(opt_addr); auto ptr = GetOffsetAs<std::shared_ptr<T>>(base_addr);
return ptr->get(); return ptr->get();
} else if (IsRawPtr()) { } else if (IsRawPtr()) {
T** ptr = static_cast<T**>(opt_addr); auto ptr = GetOffsetAs<T*>(base_addr);
return *ptr; return *ptr;
} else { } else {
return static_cast<T*>(opt_addr); return GetOffsetAs<T>(base_addr);
} }
} }
@ -675,6 +754,11 @@ class OptionTypeInfo {
const std::string& opt_name, const void* const this_ptr, const std::string& opt_name, const void* const this_ptr,
const std::string& that_value) const; const std::string& that_value) const;
Status Prepare(const ConfigOptions& config_options, const std::string& name,
void* opt_ptr) const;
Status Validate(const DBOptions& db_opts, const ColumnFamilyOptions& cf_opts,
const std::string& name, const void* opt_ptr) const;
// Parses the input opts_map according to the type_map for the opt_addr // Parses the input opts_map according to the type_map for the opt_addr
// For each name-value pair in opts_map, find the corresponding name in // For each name-value pair in opts_map, find the corresponding name in
// type_map If the name is found: // type_map If the name is found:
@ -802,6 +886,8 @@ class OptionTypeInfo {
// The optional function to match two option values // The optional function to match two option values
EqualsFunc equals_func_; EqualsFunc equals_func_;
PrepareFunc prepare_func_;
ValidateFunc validate_func_;
OptionType type_; OptionType type_;
OptionVerificationType verification_; OptionVerificationType verification_;
OptionTypeFlags flags_; OptionTypeFlags flags_;

@ -364,9 +364,10 @@ static std::unordered_map<std::string, OptionTypeInfo>
OptionTypeInfo::Struct( OptionTypeInfo::Struct(
"compaction_options_fifo", &fifo_compaction_options_type_info, "compaction_options_fifo", &fifo_compaction_options_type_info,
offsetof(struct MutableCFOptions, compaction_options_fifo), offsetof(struct MutableCFOptions, compaction_options_fifo),
OptionVerificationType::kNormal, OptionTypeFlags::kMutable, OptionVerificationType::kNormal, OptionTypeFlags::kMutable)
[](const ConfigOptions& opts, const std::string& name, .SetParseFunc([](const ConfigOptions& opts,
const std::string& value, void* addr) { const std::string& name, const std::string& value,
void* addr) {
// This is to handle backward compatibility, where // This is to handle backward compatibility, where
// compaction_options_fifo could be assigned a single scalar // compaction_options_fifo could be assigned a single scalar
// value, say, like "23", which would be assigned to // value, say, like "23", which would be assigned to
@ -556,30 +557,30 @@ static std::unordered_map<std::string, OptionTypeInfo>
{"comparator", {"comparator",
OptionTypeInfo::AsCustomRawPtr<const Comparator>( OptionTypeInfo::AsCustomRawPtr<const Comparator>(
offsetof(struct ImmutableCFOptions, user_comparator), offsetof(struct ImmutableCFOptions, user_comparator),
OptionVerificationType::kByName, OptionTypeFlags::kCompareLoose, OptionVerificationType::kByName, OptionTypeFlags::kCompareLoose)
// Serializes a Comparator .SetSerializeFunc(
[](const ConfigOptions& opts, const std::string&, const void* addr, // Serializes a Comparator
std::string* value) { [](const ConfigOptions& opts, const std::string&,
// it's a const pointer of const Comparator* const void* addr, std::string* value) {
const auto* ptr = static_cast<const Comparator* const*>(addr); // it's a const pointer of const Comparator*
const auto* ptr =
// Since the user-specified comparator will be wrapped by static_cast<const Comparator* const*>(addr);
// InternalKeyComparator, we should persist the user-specified // Since the user-specified comparator will be wrapped by
// one instead of InternalKeyComparator. // InternalKeyComparator, we should persist the
if (*ptr == nullptr) { // user-specified one instead of InternalKeyComparator.
*value = kNullptrString; if (*ptr == nullptr) {
} else if (opts.mutable_options_only) { *value = kNullptrString;
*value = ""; } else if (opts.mutable_options_only) {
} else { *value = "";
const Comparator* root_comp = (*ptr)->GetRootComparator(); } else {
if (root_comp == nullptr) { const Comparator* root_comp = (*ptr)->GetRootComparator();
root_comp = (*ptr); if (root_comp == nullptr) {
} root_comp = (*ptr);
*value = root_comp->ToString(opts); }
} *value = root_comp->ToString(opts);
return Status::OK(); }
}, return Status::OK();
/* Use the default match function*/ nullptr)}, })},
{"memtable_insert_with_hint_prefix_extractor", {"memtable_insert_with_hint_prefix_extractor",
OptionTypeInfo::AsCustomSharedPtr<const SliceTransform>( OptionTypeInfo::AsCustomSharedPtr<const SliceTransform>(
offsetof(struct ImmutableCFOptions, offsetof(struct ImmutableCFOptions,

@ -46,20 +46,10 @@ Status Configurable::PrepareOptions(const ConfigOptions& opts) {
if (opt_iter.type_map != nullptr) { if (opt_iter.type_map != nullptr) {
for (auto map_iter : *(opt_iter.type_map)) { for (auto map_iter : *(opt_iter.type_map)) {
auto& opt_info = map_iter.second; auto& opt_info = map_iter.second;
if (!opt_info.IsDeprecated() && !opt_info.IsAlias() && if (opt_info.ShouldPrepare()) {
opt_info.IsConfigurable()) { status = opt_info.Prepare(opts, map_iter.first, opt_iter.opt_ptr);
if (!opt_info.IsEnabled(OptionTypeFlags::kDontPrepare)) { if (!status.ok()) {
Configurable* config = return status;
opt_info.AsRawPointer<Configurable>(opt_iter.opt_ptr);
if (config != nullptr) {
status = config->PrepareOptions(opts);
} else if (!opt_info.CanBeNull()) {
status = Status::NotFound("Missing configurable object",
map_iter.first);
}
if (!status.ok()) {
return status;
}
} }
} }
} }
@ -79,19 +69,11 @@ Status Configurable::ValidateOptions(const DBOptions& db_opts,
if (opt_iter.type_map != nullptr) { if (opt_iter.type_map != nullptr) {
for (auto map_iter : *(opt_iter.type_map)) { for (auto map_iter : *(opt_iter.type_map)) {
auto& opt_info = map_iter.second; auto& opt_info = map_iter.second;
if (!opt_info.IsDeprecated() && !opt_info.IsAlias()) { if (opt_info.ShouldValidate()) {
if (opt_info.IsConfigurable()) { status = opt_info.Validate(db_opts, cf_opts, map_iter.first,
const Configurable* config = opt_iter.opt_ptr);
opt_info.AsRawPointer<Configurable>(opt_iter.opt_ptr); if (!status.ok()) {
if (config != nullptr) { return status;
status = config->ValidateOptions(db_opts, cf_opts);
} else if (!opt_info.CanBeNull()) {
status = Status::NotFound("Missing configurable object",
map_iter.first);
}
if (!status.ok()) {
return status;
}
} }
} }
} }

@ -76,7 +76,9 @@ bool Customizable::AreEquivalent(const ConfigOptions& config_options,
if (config_options.sanity_level > ConfigOptions::kSanityLevelNone && if (config_options.sanity_level > ConfigOptions::kSanityLevelNone &&
this != other) { this != other) {
const Customizable* custom = reinterpret_cast<const Customizable*>(other); const Customizable* custom = reinterpret_cast<const Customizable*>(other);
if (GetId() != custom->GetId()) { if (custom == nullptr) { // Cast failed
return false;
} else if (GetId() != custom->GetId()) {
*mismatch = OptionTypeInfo::kIdPropName(); *mismatch = OptionTypeInfo::kIdPropName();
return false; return false;
} else if (config_options.sanity_level > } else if (config_options.sanity_level >

@ -439,22 +439,36 @@ static std::unordered_map<std::string, OptionTypeInfo>
static_cast<int64_t>(ParseUint64(value)))); static_cast<int64_t>(ParseUint64(value))));
return Status::OK(); return Status::OK();
}}}, }}},
{"env", {"env", //**TODO: Should this be kCustomizable?
{offsetof(struct ImmutableDBOptions, env), OptionType::kUnknown, OptionTypeInfo(
OptionVerificationType::kNormal, offsetof(struct ImmutableDBOptions, env), OptionType::kUnknown,
(OptionTypeFlags::kDontSerialize | OptionTypeFlags::kCompareNever), OptionVerificationType::kNormal,
// Parse the input value as an Env (OptionTypeFlags::kDontSerialize | OptionTypeFlags::kCompareNever))
[](const ConfigOptions& opts, const std::string& /*name*/, .SetParseFunc([](const ConfigOptions& opts,
const std::string& value, void* addr) { const std::string& /*name*/,
auto old_env = static_cast<Env**>(addr); // Get the old value const std::string& value, void* addr) {
Env* new_env = *old_env; // Set new to old // Parse the input value as an Env
Status s = Env::CreateFromString(opts, value, auto old_env = static_cast<Env**>(addr); // Get the old value
&new_env); // Update new value Env* new_env = *old_env; // Set new to old
if (s.ok()) { // It worked Status s = Env::CreateFromString(opts, value,
*old_env = new_env; // Update the old one &new_env); // Update new value
} if (s.ok()) { // It worked
return s; *old_env = new_env; // Update the old one
}}}, }
return s;
})
.SetPrepareFunc([](const ConfigOptions& opts,
const std::string& /*name*/, void* addr) {
auto env = static_cast<Env**>(addr);
return (*env)->PrepareOptions(opts);
})
.SetValidateFunc([](const DBOptions& db_opts,
const ColumnFamilyOptions& cf_opts,
const std::string& /*name*/,
const void* addr) {
const auto env = static_cast<const Env* const*>(addr);
return (*env)->ValidateOptions(db_opts, cf_opts);
})},
{"allow_data_in_errors", {"allow_data_in_errors",
{offsetof(struct ImmutableDBOptions, allow_data_in_errors), {offsetof(struct ImmutableDBOptions, allow_data_in_errors),
OptionType::kBoolean, OptionVerificationType::kNormal, OptionType::kBoolean, OptionVerificationType::kNormal,

@ -898,18 +898,18 @@ Status OptionTypeInfo::Parse(const ConfigOptions& config_options,
return Status::OK(); return Status::OK();
} }
try { try {
void* opt_addr = static_cast<char*>(opt_ptr) + offset_;
const std::string& opt_value = config_options.input_strings_escaped const std::string& opt_value = config_options.input_strings_escaped
? UnescapeOptionString(value) ? UnescapeOptionString(value)
: value; : value;
if (opt_addr == nullptr) { if (opt_ptr == nullptr) {
return Status::NotFound("Could not find option", opt_name); return Status::NotFound("Could not find option", opt_name);
} else if (parse_func_ != nullptr) { } else if (parse_func_ != nullptr) {
ConfigOptions copy = config_options; ConfigOptions copy = config_options;
copy.invoke_prepare_options = false; copy.invoke_prepare_options = false;
void* opt_addr = GetOffset(opt_ptr);
return parse_func_(copy, opt_name, opt_value, opt_addr); return parse_func_(copy, opt_name, opt_value, opt_addr);
} else if (ParseOptionHelper(opt_addr, type_, opt_value)) { } else if (ParseOptionHelper(GetOffset(opt_ptr), type_, opt_value)) {
return Status::OK(); return Status::OK();
} else if (IsConfigurable()) { } else if (IsConfigurable()) {
// The option is <config>.<name> // The option is <config>.<name>
@ -1021,12 +1021,12 @@ Status OptionTypeInfo::Serialize(const ConfigOptions& config_options,
std::string* opt_value) const { std::string* opt_value) const {
// If the option is no longer used in rocksdb and marked as deprecated, // If the option is no longer used in rocksdb and marked as deprecated,
// we skip it in the serialization. // we skip it in the serialization.
const void* opt_addr = static_cast<const char*>(opt_ptr) + offset_; if (opt_ptr == nullptr || IsDeprecated()) {
if (opt_addr == nullptr || IsDeprecated()) {
return Status::OK(); return Status::OK();
} else if (IsEnabled(OptionTypeFlags::kDontSerialize)) { } else if (IsEnabled(OptionTypeFlags::kDontSerialize)) {
return Status::NotSupported("Cannot serialize option: ", opt_name); return Status::NotSupported("Cannot serialize option: ", opt_name);
} else if (serialize_func_ != nullptr) { } else if (serialize_func_ != nullptr) {
const void* opt_addr = GetOffset(opt_ptr);
return serialize_func_(config_options, opt_name, opt_addr, opt_value); return serialize_func_(config_options, opt_name, opt_addr, opt_value);
} else if (IsCustomizable()) { } else if (IsCustomizable()) {
const Customizable* custom = AsRawPointer<Customizable>(opt_ptr); const Customizable* custom = AsRawPointer<Customizable>(opt_ptr);
@ -1074,7 +1074,8 @@ Status OptionTypeInfo::Serialize(const ConfigOptions& config_options,
return Status::OK(); return Status::OK();
} else if (config_options.mutable_options_only && !IsMutable()) { } else if (config_options.mutable_options_only && !IsMutable()) {
return Status::OK(); return Status::OK();
} else if (SerializeSingleOptionHelper(opt_addr, type_, opt_value)) { } else if (SerializeSingleOptionHelper(GetOffset(opt_ptr), type_,
opt_value)) {
return Status::OK(); return Status::OK();
} else { } else {
return Status::InvalidArgument("Cannot serialize option: ", opt_name); return Status::InvalidArgument("Cannot serialize option: ", opt_name);
@ -1223,39 +1224,43 @@ bool OptionTypeInfo::AreEqual(const ConfigOptions& config_options,
if (!config_options.IsCheckEnabled(level)) { if (!config_options.IsCheckEnabled(level)) {
return true; // If the sanity level is not being checked, skip it return true; // If the sanity level is not being checked, skip it
} }
const void* this_addr = static_cast<const char*>(this_ptr) + offset_; if (this_ptr == nullptr || that_ptr == nullptr) {
const void* that_addr = static_cast<const char*>(that_ptr) + offset_; if (this_ptr == that_ptr) {
if (this_addr == nullptr || that_addr == nullptr) {
if (this_addr == that_addr) {
return true; return true;
} }
} else if (equals_func_ != nullptr) { } else if (equals_func_ != nullptr) {
const void* this_addr = GetOffset(this_ptr);
const void* that_addr = GetOffset(that_ptr);
if (equals_func_(config_options, opt_name, this_addr, that_addr, if (equals_func_(config_options, opt_name, this_addr, that_addr,
mismatch)) { mismatch)) {
return true; return true;
} }
} else if (AreOptionsEqual(type_, this_addr, that_addr)) { } else {
return true; const void* this_addr = GetOffset(this_ptr);
} else if (IsConfigurable()) { const void* that_addr = GetOffset(that_ptr);
const auto* this_config = AsRawPointer<Configurable>(this_ptr); if (AreOptionsEqual(type_, this_addr, that_addr)) {
const auto* that_config = AsRawPointer<Configurable>(that_ptr);
if (this_config == that_config) {
return true; return true;
} else if (this_config != nullptr && that_config != nullptr) { } else if (IsConfigurable()) {
std::string bad_name; const auto* this_config = AsRawPointer<Configurable>(this_ptr);
bool matches; const auto* that_config = AsRawPointer<Configurable>(that_ptr);
if (level < config_options.sanity_level) { if (this_config == that_config) {
ConfigOptions copy = config_options; return true;
copy.sanity_level = level; } else if (this_config != nullptr && that_config != nullptr) {
matches = this_config->AreEquivalent(copy, that_config, &bad_name); std::string bad_name;
} else { bool matches;
matches = if (level < config_options.sanity_level) {
this_config->AreEquivalent(config_options, that_config, &bad_name); ConfigOptions copy = config_options;
} copy.sanity_level = level;
if (!matches) { matches = this_config->AreEquivalent(copy, that_config, &bad_name);
*mismatch = opt_name + "." + bad_name; } else {
matches = this_config->AreEquivalent(config_options, that_config,
&bad_name);
}
if (!matches) {
*mismatch = opt_name + "." + bad_name;
}
return matches;
} }
return matches;
} }
} }
if (mismatch->empty()) { if (mismatch->empty()) {
@ -1379,6 +1384,44 @@ bool OptionTypeInfo::AreEqualByName(const ConfigOptions& config_options,
return (this_value == that_value); return (this_value == that_value);
} }
Status OptionTypeInfo::Prepare(const ConfigOptions& config_options,
const std::string& name, void* opt_ptr) const {
if (ShouldPrepare()) {
if (prepare_func_ != nullptr) {
void* opt_addr = GetOffset(opt_ptr);
return prepare_func_(config_options, name, opt_addr);
} else if (IsConfigurable()) {
Configurable* config = AsRawPointer<Configurable>(opt_ptr);
if (config != nullptr) {
return config->PrepareOptions(config_options);
} else if (!CanBeNull()) {
return Status::NotFound("Missing configurable object", name);
}
}
}
return Status::OK();
}
Status OptionTypeInfo::Validate(const DBOptions& db_opts,
const ColumnFamilyOptions& cf_opts,
const std::string& name,
const void* opt_ptr) const {
if (ShouldValidate()) {
if (validate_func_ != nullptr) {
const void* opt_addr = GetOffset(opt_ptr);
return validate_func_(db_opts, cf_opts, name, opt_addr);
} else if (IsConfigurable()) {
const Configurable* config = AsRawPointer<Configurable>(opt_ptr);
if (config != nullptr) {
return config->ValidateOptions(db_opts, cf_opts);
} else if (!CanBeNull()) {
return Status::NotFound("Missing configurable object", name);
}
}
}
return Status::OK();
}
const OptionTypeInfo* OptionTypeInfo::Find( const OptionTypeInfo* OptionTypeInfo::Find(
const std::string& opt_name, const std::string& opt_name,
const std::unordered_map<std::string, OptionTypeInfo>& opt_map, const std::unordered_map<std::string, OptionTypeInfo>& opt_map,

@ -4266,19 +4266,20 @@ TEST_F(OptionTypeInfoTest, TestInvalidArgs) {
} }
TEST_F(OptionTypeInfoTest, TestParseFunc) { TEST_F(OptionTypeInfoTest, TestParseFunc) {
OptionTypeInfo opt_info( OptionTypeInfo opt_info(0, OptionType::kUnknown,
0, OptionType::kUnknown, OptionVerificationType::kNormal, OptionVerificationType::kNormal,
OptionTypeFlags::kNone, OptionTypeFlags::kNone);
[](const ConfigOptions& /*opts*/, const std::string& name, opt_info.SetParseFunc([](const ConfigOptions& /*opts*/,
const std::string& value, void* addr) { const std::string& name, const std::string& value,
auto ptr = static_cast<std::string*>(addr); void* addr) {
if (name == "Oops") { auto ptr = static_cast<std::string*>(addr);
return Status::InvalidArgument(value); if (name == "Oops") {
} else { return Status::InvalidArgument(value);
*ptr = value + " " + name; } else {
return Status::OK(); *ptr = value + " " + name;
} return Status::OK();
}); }
});
ConfigOptions config_options; ConfigOptions config_options;
std::string base; std::string base;
ASSERT_OK(opt_info.Parse(config_options, "World", "Hello", &base)); ASSERT_OK(opt_info.Parse(config_options, "World", "Hello", &base));
@ -4287,19 +4288,19 @@ TEST_F(OptionTypeInfoTest, TestParseFunc) {
} }
TEST_F(OptionTypeInfoTest, TestSerializeFunc) { TEST_F(OptionTypeInfoTest, TestSerializeFunc) {
OptionTypeInfo opt_info( OptionTypeInfo opt_info(0, OptionType::kString,
0, OptionType::kString, OptionVerificationType::kNormal, OptionVerificationType::kNormal,
OptionTypeFlags::kNone, nullptr, OptionTypeFlags::kNone);
[](const ConfigOptions& /*opts*/, const std::string& name, opt_info.SetSerializeFunc([](const ConfigOptions& /*opts*/,
const void* /*addr*/, std::string* value) { const std::string& name, const void* /*addr*/,
if (name == "Oops") { std::string* value) {
return Status::InvalidArgument(name); if (name == "Oops") {
} else { return Status::InvalidArgument(name);
*value = name; } else {
return Status::OK(); *value = name;
} return Status::OK();
}, }
nullptr); });
ConfigOptions config_options; ConfigOptions config_options;
std::string base; std::string base;
std::string value; std::string value;
@ -4309,24 +4310,24 @@ TEST_F(OptionTypeInfoTest, TestSerializeFunc) {
} }
TEST_F(OptionTypeInfoTest, TestEqualsFunc) { TEST_F(OptionTypeInfoTest, TestEqualsFunc) {
OptionTypeInfo opt_info( OptionTypeInfo opt_info(0, OptionType::kInt, OptionVerificationType::kNormal,
0, OptionType::kInt, OptionVerificationType::kNormal, OptionTypeFlags::kNone);
OptionTypeFlags::kNone, nullptr, nullptr, opt_info.SetEqualsFunc([](const ConfigOptions& /*opts*/,
[](const ConfigOptions& /*opts*/, const std::string& name, const std::string& name, const void* addr1,
const void* addr1, const void* addr2, std::string* mismatch) { const void* addr2, std::string* mismatch) {
auto i1 = *(static_cast<const int*>(addr1)); auto i1 = *(static_cast<const int*>(addr1));
auto i2 = *(static_cast<const int*>(addr2)); auto i2 = *(static_cast<const int*>(addr2));
if (name == "LT") { if (name == "LT") {
return i1 < i2; return i1 < i2;
} else if (name == "GT") { } else if (name == "GT") {
return i1 > i2; return i1 > i2;
} else if (name == "EQ") { } else if (name == "EQ") {
return i1 == i2; return i1 == i2;
} else { } else {
*mismatch = name + "???"; *mismatch = name + "???";
return false; return false;
} }
}); });
ConfigOptions config_options; ConfigOptions config_options;
int int1 = 100; int int1 = 100;
@ -4342,6 +4343,64 @@ TEST_F(OptionTypeInfoTest, TestEqualsFunc) {
ASSERT_EQ(mismatch, "NO???"); ASSERT_EQ(mismatch, "NO???");
} }
TEST_F(OptionTypeInfoTest, TestPrepareFunc) {
OptionTypeInfo opt_info(0, OptionType::kInt, OptionVerificationType::kNormal,
OptionTypeFlags::kNone);
opt_info.SetPrepareFunc(
[](const ConfigOptions& /*opts*/, const std::string& name, void* addr) {
auto i1 = static_cast<int*>(addr);
if (name == "x2") {
*i1 *= 2;
} else if (name == "/2") {
*i1 /= 2;
} else {
return Status::InvalidArgument("Bad Argument", name);
}
return Status::OK();
});
ConfigOptions config_options;
int int1 = 100;
ASSERT_OK(opt_info.Prepare(config_options, "x2", &int1));
ASSERT_EQ(int1, 200);
ASSERT_OK(opt_info.Prepare(config_options, "/2", &int1));
ASSERT_EQ(int1, 100);
ASSERT_NOK(opt_info.Prepare(config_options, "??", &int1));
ASSERT_EQ(int1, 100);
}
TEST_F(OptionTypeInfoTest, TestValidateFunc) {
OptionTypeInfo opt_info(0, OptionType::kSizeT,
OptionVerificationType::kNormal,
OptionTypeFlags::kNone);
opt_info.SetValidateFunc([](const DBOptions& db_opts,
const ColumnFamilyOptions& cf_opts,
const std::string& name, const void* addr) {
const auto sz = static_cast<const size_t*>(addr);
bool is_valid = false;
if (name == "keep_log_file_num") {
is_valid = (*sz == db_opts.keep_log_file_num);
} else if (name == "write_buffer_size") {
is_valid = (*sz == cf_opts.write_buffer_size);
}
if (is_valid) {
return Status::OK();
} else {
return Status::InvalidArgument("Mismatched value", name);
}
});
ConfigOptions config_options;
DBOptions db_options;
ColumnFamilyOptions cf_options;
ASSERT_OK(opt_info.Validate(db_options, cf_options, "keep_log_file_num",
&db_options.keep_log_file_num));
ASSERT_OK(opt_info.Validate(db_options, cf_options, "write_buffer_size",
&cf_options.write_buffer_size));
ASSERT_NOK(opt_info.Validate(db_options, cf_options, "keep_log_file_num",
&cf_options.write_buffer_size));
ASSERT_NOK(opt_info.Validate(db_options, cf_options, "write_buffer_size",
&db_options.keep_log_file_num));
}
TEST_F(OptionTypeInfoTest, TestOptionFlags) { TEST_F(OptionTypeInfoTest, TestOptionFlags) {
OptionTypeInfo opt_none(0, OptionType::kString, OptionTypeInfo opt_none(0, OptionType::kString,
OptionVerificationType::kNormal, OptionVerificationType::kNormal,

Loading…
Cancel
Save